refactor(web): update text and descriptions for clarity and consistency across various components

This commit is contained in:
Bjorn Lammers 2025-04-22 23:55:40 +02:00
parent 293f67bcc8
commit d6cb15aab0
11 changed files with 134 additions and 87 deletions

View File

@ -32,16 +32,16 @@ export default function ErrorPage({
</div> </div>
<h1 className="text-2xl font-bold">Something went wrong</h1> <h1 className="text-2xl font-bold">Something went wrong</h1>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
An unexpected error occurred while loading this page. We've been notified and are looking into it. Unable to load this page. We're looking into the issue.
</p> </p>
<div className="flex flex-col sm:flex-row gap-4 justify-center pt-4"> <div className="flex flex-col sm:flex-row gap-4 justify-center pt-4">
<Button variant="outline" onClick={() => reset()} className="cursor-pointer"> <Button variant="outline" onClick={() => reset()} className="cursor-pointer">
<RefreshCcw className="mr-2 h-4 w-4" /> <RefreshCcw className="mr-2 h-4 w-4" />
Try again Retry
</Button> </Button>
<Button onClick={handleGoBack} className="cursor-pointer"> <Button onClick={handleGoBack} className="cursor-pointer">
<ArrowLeft className="mr-2 h-4 w-4" /> <ArrowLeft className="mr-2 h-4 w-4" />
Go back Back
</Button> </Button>
</div> </div>
{error.digest && <p className="text-xs text-muted-foreground mt-6">Error ID: {error.digest}</p>} {error.digest && <p className="text-xs text-muted-foreground mt-6">Error ID: {error.digest}</p>}

View File

@ -42,7 +42,7 @@ export async function generateMetadata({ params, searchParams }: Props, parent:
return { return {
title: `${formattedIconName} Icon | Dashboard Icons`, title: `${formattedIconName} Icon | Dashboard Icons`,
description: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats for FREE. Part of a collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`, description: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats. Available for free in our collection of ${totalIcons} icons for dashboards and applications.`,
assets: [iconImageUrl], assets: [iconImageUrl],
category: "icons", category: "icons",
keywords: [ keywords: [
@ -57,14 +57,14 @@ export async function generateMetadata({ params, searchParams }: Props, parent:
icons: { icons: {
icon: iconImageUrl, icon: iconImageUrl,
}, },
abstract: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats for FREE. Part of a collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`, abstract: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats. Available for free in our collection of ${totalIcons} icons for dashboards and applications.`,
robots: { robots: {
index: true, index: true,
follow: true, follow: true,
}, },
openGraph: { openGraph: {
title: `${formattedIconName} Icon | Dashboard Icons`, title: `${formattedIconName} Icon | Dashboard Icons`,
description: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats for FREE. Part of a collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`, description: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats. Available for free in our collection of ${totalIcons} icons for dashboards and applications.`,
type: "article", type: "article",
url: pageUrl, url: pageUrl,
authors: [authorName], authors: [authorName],
@ -76,7 +76,7 @@ export async function generateMetadata({ params, searchParams }: Props, parent:
twitter: { twitter: {
card: "summary_large_image", card: "summary_large_image",
title: `${formattedIconName} Icon | Dashboard Icons`, title: `${formattedIconName} Icon | Dashboard Icons`,
description: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats for FREE. Part of a collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`, description: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats. Available for free in our collection of ${totalIcons} icons for dashboards and applications.`,
images: [iconImageUrl], images: [iconImageUrl],
}, },
alternates: { alternates: {

View File

@ -228,11 +228,11 @@ export function IconSearch({ icons }: IconSearchProps) {
const getSortLabel = (sort: SortOption) => { const getSortLabel = (sort: SortOption) => {
switch (sort) { switch (sort) {
case "relevance": case "relevance":
return "Best match" return "Relevance"
case "alphabetical-asc": case "alphabetical-asc":
return "A to Z" return "Name (A-Z)"
case "alphabetical-desc": case "alphabetical-desc":
return "Z to A" return "Name (Z-A)"
case "newest": case "newest":
return "Newest first" return "Newest first"
default: default:
@ -265,7 +265,7 @@ export function IconSearch({ icons }: IconSearchProps) {
</div> </div>
<Input <Input
type="search" type="search"
placeholder="Search icons by name, alias, or category..." placeholder="Search for icons..."
className="w-full h-10 pl-9 cursor-text transition-all duration-300 text-sm md:text-base border-border shadow-sm" className="w-full h-10 pl-9 cursor-text transition-all duration-300 text-sm md:text-base border-border shadow-sm"
value={searchQuery} value={searchQuery}
onChange={(e) => handleSearch(e.target.value)} onChange={(e) => handleSearch(e.target.value)}
@ -279,7 +279,7 @@ export function IconSearch({ icons }: IconSearchProps) {
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="flex-1 sm:flex-none cursor-pointer bg-background border-border shadow-sm "> <Button variant="outline" size="sm" className="flex-1 sm:flex-none cursor-pointer bg-background border-border shadow-sm ">
<Filter className="h-4 w-4 mr-2" /> <Filter className="h-4 w-4 mr-2" />
<span>Filter</span> <span>Categories</span>
{selectedCategories.length > 0 && ( {selectedCategories.length > 0 && (
<Badge variant="secondary" className="ml-2 px-1.5"> <Badge variant="secondary" className="ml-2 px-1.5">
{selectedCategories.length} {selectedCategories.length}
@ -288,7 +288,7 @@ export function IconSearch({ icons }: IconSearchProps) {
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-64 sm:w-56"> <DropdownMenuContent align="start" className="w-64 sm:w-56">
<DropdownMenuLabel className="font-semibold">Categories</DropdownMenuLabel> <DropdownMenuLabel className="font-semibold">Select Categories</DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<div className="max-h-[40vh] overflow-y-auto p-1"> <div className="max-h-[40vh] overflow-y-auto p-1">
@ -314,7 +314,7 @@ export function IconSearch({ icons }: IconSearchProps) {
}} }}
className="cursor-pointer focus: focus:bg-rose-50 dark:focus:bg-rose-950/20" className="cursor-pointer focus: focus:bg-rose-50 dark:focus:bg-rose-950/20"
> >
Clear all filters Clear categories
</DropdownMenuItem> </DropdownMenuItem>
</> </>
)} )}
@ -330,18 +330,18 @@ export function IconSearch({ icons }: IconSearchProps) {
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-56"> <DropdownMenuContent align="start" className="w-56">
<DropdownMenuLabel className="font-semibold">Sort By</DropdownMenuLabel> <DropdownMenuLabel className="font-semibold">Sort Icons</DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuRadioGroup value={sortOption} onValueChange={(value) => handleSortChange(value as SortOption)}> <DropdownMenuRadioGroup value={sortOption} onValueChange={(value) => handleSortChange(value as SortOption)}>
<DropdownMenuRadioItem value="relevance" className="cursor-pointer"> <DropdownMenuRadioItem value="relevance" className="cursor-pointer">
<Search className="h-4 w-4 mr-2" /> <Search className="h-4 w-4 mr-2" />
Best match Relevance
</DropdownMenuRadioItem> </DropdownMenuRadioItem>
<DropdownMenuRadioItem value="alphabetical-asc" className="cursor-pointer"> <DropdownMenuRadioItem value="alphabetical-asc" className="cursor-pointer">
<ArrowDownAZ className="h-4 w-4 mr-2" />A to Z <ArrowDownAZ className="h-4 w-4 mr-2" />Name (A-Z)
</DropdownMenuRadioItem> </DropdownMenuRadioItem>
<DropdownMenuRadioItem value="alphabetical-desc" className="cursor-pointer"> <DropdownMenuRadioItem value="alphabetical-desc" className="cursor-pointer">
<ArrowUpZA className="h-4 w-4 mr-2" />Z to A <ArrowUpZA className="h-4 w-4 mr-2" />Name (Z-A)
</DropdownMenuRadioItem> </DropdownMenuRadioItem>
<DropdownMenuRadioItem value="newest" className="cursor-pointer"> <DropdownMenuRadioItem value="newest" className="cursor-pointer">
<Calendar className="h-4 w-4 mr-2" /> <Calendar className="h-4 w-4 mr-2" />
@ -355,7 +355,7 @@ export function IconSearch({ icons }: IconSearchProps) {
{(searchQuery || selectedCategories.length > 0 || sortOption !== "relevance") && ( {(searchQuery || selectedCategories.length > 0 || sortOption !== "relevance") && (
<Button variant="outline" size="sm" onClick={clearFilters} className="flex-1 sm:flex-none cursor-pointer bg-background"> <Button variant="outline" size="sm" onClick={clearFilters} className="flex-1 sm:flex-none cursor-pointer bg-background">
<X className="h-4 w-4 mr-2" /> <X className="h-4 w-4 mr-2" />
<span>Clear all</span> <span>Reset</span>
</Button> </Button>
)} )}
</div> </div>
@ -363,7 +363,7 @@ export function IconSearch({ icons }: IconSearchProps) {
{/* Active filter badges */} {/* Active filter badges */}
{selectedCategories.length > 0 && ( {selectedCategories.length > 0 && (
<div className="flex flex-wrap items-center gap-2 mt-2"> <div className="flex flex-wrap items-center gap-2 mt-2">
<span className="text-sm text-muted-foreground">Filters:</span> <span className="text-sm text-muted-foreground">Selected:</span>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{selectedCategories.map((category) => ( {selectedCategories.map((category) => (
<Badge key={category} variant="secondary" className="flex items-center gap-1 pl-2 pr-1"> <Badge key={category} variant="secondary" className="flex items-center gap-1 pl-2 pr-1">
@ -389,7 +389,7 @@ export function IconSearch({ icons }: IconSearchProps) {
}} }}
className="text-xs h-7 px-2 cursor-pointer" className="text-xs h-7 px-2 cursor-pointer"
> >
Clear all Clear
</Button> </Button>
</div> </div>
)} )}
@ -400,16 +400,21 @@ export function IconSearch({ icons }: IconSearchProps) {
{filteredIcons.length === 0 ? ( {filteredIcons.length === 0 ? (
<div className="flex flex-col gap-8 py-12 max-w-2xl mx-auto items-center"> <div className="flex flex-col gap-8 py-12 max-w-2xl mx-auto items-center">
<div className="text-center"> <div className="text-center">
<h2 className="text-3xl sm:text-5xl font-semibold">We don't have this one...yet!</h2> <h2 className="text-3xl sm:text-5xl font-semibold">Icon not found</h2>
<p className="text-lg text-muted-foreground mt-2">Help us expand our collection</p>
</div> </div>
<div className="flex flex-col gap-4 items-center w-full">
<IconSubmissionContent />
<div className="mt-4 flex items-center gap-2 justify-center">
<span className="text-sm text-muted-foreground">Need help?</span>
<Button <Button
className="cursor-pointer motion-preset-pop" className="cursor-pointer"
variant="default" variant="outline"
size="lg" size="sm"
onClick={() => { onClick={() => {
setIsLazyRequestSubmitted(true) setIsLazyRequestSubmitted(true)
toast("We hear you!", { toast("Request submitted", {
description: `Okay, okay... we'll consider adding "${searchQuery || "that icon"}" just for you. 😉`, description: `We've added "${searchQuery || "this icon"}" to our request list.`,
}) })
posthog.capture("lazy icon request", { posthog.capture("lazy icon request", {
query: searchQuery, query: searchQuery,
@ -418,16 +423,16 @@ export function IconSearch({ icons }: IconSearchProps) {
}} }}
disabled={isLazyRequestSubmitted} disabled={isLazyRequestSubmitted}
> >
I want this icon added but I'm too lazy to add it myself Submit request
</Button> </Button>
<IconSubmissionContent /> </div>
</div>
</div> </div>
) : ( ) : (
<> <>
<div className="flex justify-between items-center pb-2"> <div className="flex justify-between items-center pb-2">
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Found {filteredIcons.length} icon {filteredIcons.length} {filteredIcons.length === 1 ? "icon" : "icons"} found
{filteredIcons.length !== 1 ? "s" : ""}.
</p> </p>
<div className="flex items-center gap-1 text-xs text-muted-foreground"> <div className="flex items-center gap-1 text-xs text-muted-foreground">
{getSortIcon(sortOption)} {getSortIcon(sortOption)}
@ -445,11 +450,9 @@ export function IconSearch({ icons }: IconSearchProps) {
function IconCard({ function IconCard({
name, name,
data: iconData, data: iconData,
matchedAlias,
}: { }: {
name: string name: string
data: Icon data: Icon
matchedAlias?: string | null
}) { }) {
return ( return (
<MagicCard className="rounded-md shadow-md"> <MagicCard className="rounded-md shadow-md">
@ -465,8 +468,6 @@ function IconCard({
<span className="text-xs sm:text-sm text-center truncate w-full capitalize group- dark:group-hover:text-rose-400 transition-colors duration-200 font-medium"> <span className="text-xs sm:text-sm text-center truncate w-full capitalize group- dark:group-hover:text-rose-400 transition-colors duration-200 font-medium">
{name.replace(/-/g, " ")} {name.replace(/-/g, " ")}
</span> </span>
{matchedAlias && <span className="text-[10px] text-center truncate w-full mt-1">Alias: {matchedAlias}</span>}
</Link> </Link>
</MagicCard> </MagicCard>
) )
@ -482,10 +483,10 @@ function IconsGrid({ filteredIcons, matchedAliases }: IconsGridProps) {
<> <>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-4 mt-2"> <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-4 mt-2">
{filteredIcons.slice(0, 120).map(({ name, data }) => ( {filteredIcons.slice(0, 120).map(({ name, data }) => (
<IconCard key={name} name={name} data={data} matchedAlias={matchedAliases[name] || null} /> <IconCard key={name} name={name} data={data} />
))} ))}
</div> </div>
{filteredIcons.length > 120 && <p className="text-sm text-muted-foreground">And {filteredIcons.length - 120} more...</p>} {filteredIcons.length > 120 && <p className="text-sm text-muted-foreground">{filteredIcons.length - 120} more icons available</p>}
</> </>
) )
} }

View File

@ -9,7 +9,7 @@ export async function generateMetadata(): Promise<Metadata> {
return { return {
title: "Browse Icons | Free Dashboard Icons", title: "Browse Icons | Free Dashboard Icons",
description: `Search and browse through our collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`, description: `Browse our collection of ${totalIcons} icons for dashboards and applications. Available in SVG, PNG, and WEBP formats.`,
keywords: [ keywords: [
"browse icons", "browse icons",
"dashboard icons", "dashboard icons",
@ -22,7 +22,7 @@ export async function generateMetadata(): Promise<Metadata> {
], ],
openGraph: { openGraph: {
title: "Browse Icons | Free Dashboard Icons", title: "Browse Icons | Free Dashboard Icons",
description: `Search and browse through our collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`, description: `Browse our collection of ${totalIcons} icons for dashboards and applications. Available in SVG, PNG, and WEBP formats.`,
type: "website", type: "website",
url: `${BASE_URL}/icons`, url: `${BASE_URL}/icons`,
images: [ images: [
@ -38,7 +38,7 @@ export async function generateMetadata(): Promise<Metadata> {
twitter: { twitter: {
card: "summary_large_image", card: "summary_large_image",
title: "Browse Icons | Free Dashboard Icons", title: "Browse Icons | Free Dashboard Icons",
description: `Search and browse through our collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`, description: `Browse our collection of ${totalIcons} icons for dashboards and applications. Available in SVG, PNG, and WEBP formats.`,
images: ["/og-image-browse.png"], images: ["/og-image-browse.png"],
}, },
alternates: { alternates: {
@ -57,8 +57,8 @@ export default async function IconsPage() {
<div className="space-y-4 mb-8 mx-auto max-w-7xl"> <div className="space-y-4 mb-8 mx-auto max-w-7xl">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4"> <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div> <div>
<h1 className="text-3xl font-bold">Browse icons</h1> <h1 className="text-3xl font-bold">Icons</h1>
<p className="text-muted-foreground">Search through our collection of {icons.length} beautiful icons.</p> <p className="text-muted-foreground">Search our collection of {icons.length} icons.</p>
</div> </div>
</div> </div>

View File

@ -15,9 +15,9 @@ export default function NotFound({
<div className="mx-auto w-16 h-16 bg-red-100 dark:bg-red-900/20 rounded-full flex items-center justify-center text-red-600 dark:text-red-400"> <div className="mx-auto w-16 h-16 bg-red-100 dark:bg-red-900/20 rounded-full flex items-center justify-center text-red-600 dark:text-red-400">
<AlertTriangle className="w-8 h-8" /> <AlertTriangle className="w-8 h-8" />
</div> </div>
<h1 className="text-2xl sm:text-3xl font-bold mt-6">Icon not found</h1> <h1 className="text-2xl sm:text-3xl font-bold mt-6">Not found</h1>
<p className="text-muted-foreground mt-3 max-w-md"> <p className="text-muted-foreground mt-3 max-w-md">
The icon you are looking for could not be found or there was an error loading it. This icon does not exist or could not be loaded.
</p> </p>
</div> </div>
@ -25,16 +25,16 @@ export default function NotFound({
<Button asChild variant="outline"> <Button asChild variant="outline">
<Link href="/icons"> <Link href="/icons">
<ArrowLeft className="mr-2 h-4 w-4" /> <ArrowLeft className="mr-2 h-4 w-4" />
Back to all icons Back to icons
</Link> </Link>
</Button> </Button>
</div> </div>
<div className="border-t border-border pt-8 mt-8"> <div className="border-t border-border pt-8 mt-8">
<div className="text-center mb-6"> <div className="text-center mb-6">
<h2 className="text-xl font-semibold">Can't find what you're looking for?</h2> <h2 className="text-xl font-semibold">Missing an icon?</h2>
<p className="text-muted-foreground mt-2"> <p className="text-muted-foreground mt-2">
Contribute to our icon collection by suggesting a new icon or improving an existing one. Submit a new icon or suggest improvements to our collection.
</p> </p>
</div> </div>

View File

@ -37,7 +37,7 @@ export function Footer() {
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<h3 className="font-bold text-lg text-foreground/90">Dashboard Icons</h3> <h3 className="font-bold text-lg text-foreground/90">Dashboard Icons</h3>
<p className="text-sm text-muted-foreground leading-relaxed"> <p className="text-sm text-muted-foreground leading-relaxed">
A collection of curated icons for services, applications and tools, designed specifically for dashboards and app directories. Collection of icons for applications, services, and tools - designed for dashboards and app directories.
</p> </p>
</div> </div>

View File

@ -209,9 +209,57 @@ export function HeroSection({ totalIcons, stars }: { totalIcons: number; stars:
<div className="max-w-4xl mx-auto text-center flex flex-col gap-4 "> <div className="max-w-4xl mx-auto text-center flex flex-col gap-4 ">
<h1 className="relative text-3xl sm:text-5xl md:text-7xl font-bold mb-4 md:mb-8 tracking-tight motion-preset-slide-up motion-duration-500 "> <h1 className="relative text-3xl sm:text-5xl md:text-7xl font-bold mb-4 md:mb-8 tracking-tight motion-preset-slide-up motion-duration-500 ">
Your definitive source for Your definitive source for
<Sparkles className="absolute -right-1 -bottom-3 text-rose-500 h-8 w-8 sm:h-12 sm:w-12 md:h-16 md:w-12 motion-delay-300 motion-preset-seesaw-lg motion-scale-in-[0.5] motion-translate-x-in-[-120%] motion-translate-y-in-[-60%] motion-opacity-in-[33%] motion-rotate-in-[-1080deg] motion-blur-in-[10px] motion-duration-500 motion-delay-[0.13s]/scale motion-duration-[0.13s]/opacity motion-duration-[0.40s]/rotate motion-duration-[0.05s]/blur motion-delay-[0.20s]/blur motion-ease-spring-bouncier" /> <motion.span
className="absolute -right-1 -bottom-3"
initial={{ opacity: 0, scale: 0.5, x: -20, y: -10 }}
animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}
transition={{
duration: 0.5,
delay: 0.3,
ease: "easeOut"
}}
>
<motion.div
animate={{
y: [0, -3, 0],
rotate: [0, 5, 0]
}}
transition={{
duration: 3,
repeat: Infinity,
repeatType: "reverse",
ease: "easeInOut"
}}
>
<Sparkles className="text-rose-500 h-8 w-8 sm:h-12 sm:w-12 md:h-16 md:w-12" />
</motion.div>
</motion.span>
<br /> <br />
<Sparkles className="absolute -left-1 -top-3 text-rose-500 h-5 w-5 sm:h-8 sm:w-8 md:h-12 md:w-12 motion-delay-300 motion-preset-seesaw-lg motion-scale-in-[0.5] motion-translate-x-in-[159%] motion-translate-y-in-[-60%] motion-opacity-in-[33%] motion-rotate-in-[-1080deg] motion-blur-in-[10px] motion-duration-500 motion-delay-[0.13s]/scale motion-duration-[0.13s]/opacity motion-duration-[0.40s]/rotate motion-duration-[0.05s]/blur motion-delay-[0.20s]/blur motion-ease-spring-bouncier" /> <motion.span
className="absolute -left-1 -top-3"
initial={{ opacity: 0, scale: 0.5, x: 20, y: -10 }}
animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}
transition={{
duration: 0.5,
delay: 0.3,
ease: "easeOut"
}}
>
<motion.div
animate={{
y: [0, -3, 0],
rotate: [0, -5, 0]
}}
transition={{
duration: 4,
repeat: Infinity,
repeatType: "reverse",
ease: "easeInOut"
}}
>
<Sparkles className="text-rose-500 h-5 w-5 sm:h-8 sm:w-8 md:h-12 md:w-12" />
</motion.div>
</motion.span>
<AuroraText colors={["#FA5352", "#FA5352", "orange"]}>dashboard icons</AuroraText> <AuroraText colors={["#FA5352", "#FA5352", "orange"]}>dashboard icons</AuroraText>
</h1> </h1>
@ -224,7 +272,7 @@ export function HeroSection({ totalIcons, stars }: { totalIcons: number; stars:
<SearchInput searchQuery={searchQuery} setSearchQuery={setSearchQuery} totalIcons={totalIcons} /> <SearchInput searchQuery={searchQuery} setSearchQuery={setSearchQuery} totalIcons={totalIcons} />
<div className="w-full flex gap-3 md:gap-4 flex-wrap justify-center motion-preset-slide-down motion-duration-500"> <div className="w-full flex gap-3 md:gap-4 flex-wrap justify-center motion-preset-slide-down motion-duration-500">
<Link href="/icons"> <Link href="/icons">
<InteractiveHoverButton className="rounded-md bg-input/30">Explore icons</InteractiveHoverButton> <InteractiveHoverButton className="rounded-md bg-input/30">Browse icons</InteractiveHoverButton>
</Link> </Link>
<GiveUsAStarButton stars={stars} /> <GiveUsAStarButton stars={stars} />
<GiveUsMoneyButton /> <GiveUsMoneyButton />
@ -449,12 +497,12 @@ export function GiveUsMoneyButton() {
<div className="flex justify-between items-center pt-2"> <div className="flex justify-between items-center pt-2">
<Link href={openCollectiveUrl} target="_blank" rel="noopener noreferrer"> <Link href={openCollectiveUrl} target="_blank" rel="noopener noreferrer">
<Button variant="default" size="sm" className="bg-primary hover:bg-primary/90"> <Button variant="default" size="sm" className="bg-primary hover:bg-primary/90">
Donate Support
</Button> </Button>
</Link> </Link>
<Link href={`${openCollectiveUrl}/transactions`} target="_blank" rel="noopener noreferrer"> <Link href={`${openCollectiveUrl}/transactions`} target="_blank" rel="noopener noreferrer">
<Button variant="link" size="sm" className="flex items-center gap-1 text-xs text-secondary-foreground"> <Button variant="link" size="sm" className="flex items-center gap-1 text-xs text-secondary-foreground">
View expenses View transactions
<ExternalLink className="h-3 w-3" /> <ExternalLink className="h-3 w-3" />
</Button> </Button>
</Link> </Link>
@ -478,7 +526,7 @@ function SearchInput({ searchQuery, setSearchQuery, totalIcons }: SearchInputPro
name="q" name="q"
autoFocus autoFocus
type="search" type="search"
placeholder={`Find any of ${totalIcons} icons by name or category...`} placeholder={`Search from ${totalIcons} icons...`}
className="pl-10 h-10 md:h-12 rounded-lg w-full border-border focus:border-primary/20 text-sm md:text-base" className="pl-10 h-10 md:h-12 rounded-lg w-full border-border focus:border-primary/20 text-sm md:text-base"
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}

View File

@ -347,16 +347,14 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) {
<h3 className="text-sm font-semibold text-muted-foreground">About this icon</h3> <h3 className="text-sm font-semibold text-muted-foreground">About this icon</h3>
<div className="text-xs text-muted-foreground space-y-2"> <div className="text-xs text-muted-foreground space-y-2">
<p> <p>
Available in{" "} Available in {availableFormats.length > 1
{availableFormats.length > 1
? `${availableFormats.length} formats (${availableFormats.map((f) => f.toUpperCase()).join(", ")}) ` ? `${availableFormats.length} formats (${availableFormats.map((f) => f.toUpperCase()).join(", ")}) `
: `${availableFormats[0].toUpperCase()} format`}{" "} : `${availableFormats[0].toUpperCase()} format `}
with a base format of {iconData.base.toUpperCase()}. with a base format of {iconData.base.toUpperCase()}.
{iconData.colors && " Includes both light and dark theme variants for better integration with different UI designs."} {iconData.colors && " Includes both light and dark theme variants for better integration with different UI designs."}
</p> </p>
<p> <p>
Use the {icon} icon in your web applications, dashboards, or documentation to enhance visual communication and user Perfect for adding to dashboards, app directories, documentation, or anywhere you need the {icon.replace(/-/g, " ")} logo.
experience.
</p> </p>
</div> </div>
</div> </div>

View File

@ -11,32 +11,32 @@ import { useState } from "react"
export const ISSUE_TEMPLATES = [ export const ISSUE_TEMPLATES = [
{ {
id: "add_monochrome_icon", id: "add_monochrome_icon",
name: "Add light & dark icon", name: "Add light/dark icon",
description: "Submit a new icon with both light and dark versions for optimal theme compatibility.", description: "Submit a new icon with light and dark versions.",
url: `${REPO_PATH}/issues/new?template=add_monochrome_icon.yml`, url: `${REPO_PATH}/issues/new?template=add_monochrome_icon.yml`,
}, },
{ {
id: "add_normal_icon", id: "add_normal_icon",
name: "Add normal icon", name: "Add standard icon",
description: "Submit a new icon that works well across both light and dark themes.", description: "Submit a new icon for both themes.",
url: `${REPO_PATH}/issues/new?template=add_normal_icon.yml`, url: `${REPO_PATH}/issues/new?template=add_normal_icon.yml`,
}, },
{ {
id: "update_monochrome_icon", id: "update_monochrome_icon",
name: "Update light & dark icon", name: "Update light/dark icon",
description: "Improve an existing icon by updating both light and dark versions.", description: "Improve an existing light/dark icon.",
url: `${REPO_PATH}/issues/new?template=update_monochrome_icon.yml`, url: `${REPO_PATH}/issues/new?template=update_monochrome_icon.yml`,
}, },
{ {
id: "update_normal_icon", id: "update_normal_icon",
name: "Update normal icon", name: "Update standard icon",
description: "Improve an existing icon that works across both light and dark themes.", description: "Improve an existing standard icon.",
url: `${REPO_PATH}/issues/new?template=update_normal_icon.yml`, url: `${REPO_PATH}/issues/new?template=update_normal_icon.yml`,
}, },
{ {
id: "blank_issue", id: "blank_issue",
name: "Something else", name: "Other request",
description: "Create a custom issue for other suggestions, bug reports, or improvements.", description: "Submit another type of request.",
url: `${REPO_PATH}/issues/new?template=BLANK_ISSUE`, url: `${REPO_PATH}/issues/new?template=BLANK_ISSUE`,
}, },
] ]
@ -73,13 +73,13 @@ export function IconSubmissionForm() {
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="outline" className="hidden md:inline-flex cursor-pointer transition-all duration-300"> <Button variant="outline" className="hidden md:inline-flex cursor-pointer transition-all duration-300">
<PlusCircle className="h-4 w-4 transition-all duration-300" /> Contribute new icon <PlusCircle className="h-4 w-4 transition-all duration-300" /> Submit icon
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="md:max-w-4xl backdrop-blur-2xl bg-background"> <DialogContent className="md:max-w-4xl backdrop-blur-2xl bg-background">
<DialogHeader> <DialogHeader>
<DialogTitle>Contribute a new icon</DialogTitle> <DialogTitle>Submit an icon</DialogTitle>
<DialogDescription>Choose a template below to suggest a new icon or improve an existing one.</DialogDescription> <DialogDescription>Select an option below to submit or update an icon.</DialogDescription>
</DialogHeader> </DialogHeader>
<div className="mt-4"> <div className="mt-4">
<IconSubmissionContent onClose={() => setOpen(false)} /> <IconSubmissionContent onClose={() => setOpen(false)} />

View File

@ -61,7 +61,7 @@ export function RecentlyAddedIcons({ icons }: { icons: IconWithName[] }) {
href="/icons" href="/icons"
className="font-medium inline-flex items-center py-2 px-4 rounded-full border transition-all duration-200 group hover-lift soft-shadow" className="font-medium inline-flex items-center py-2 px-4 rounded-full border transition-all duration-200 group hover-lift soft-shadow"
> >
View complete collection View all icons
<ArrowRight className="w-4 h-4 ml-1.5 transition-transform duration-200 group-hover:translate-x-1" /> <ArrowRight className="w-4 h-4 ml-1.5 transition-transform duration-200 group-hover:translate-x-1" />
</Link> </Link>
</div> </div>

View File

@ -5,6 +5,6 @@ export const WEB_URL = "https://dashboardicons.com"
export const REPO_NAME = "homarr-labs/dashboard-icons" export const REPO_NAME = "homarr-labs/dashboard-icons"
export const getDescription = (totalIcons: number) => export const getDescription = (totalIcons: number) =>
`A collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.` `Collection of ${totalIcons} icons for applications, services, and tools - designed for dashboards and app directories.`
export const websiteTitle = "Free Dashboard Icons - Download High-Quality UI & App Icons" export const websiteTitle = "Free Dashboard Icons - Download High-Quality UI & App Icons"