-
Notifications
You must be signed in to change notification settings - Fork 0
feat: button component #26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
The preview deployment is ready. 🟢 Open Preview | Open Build Logs Last updated at: 2025-07-10 22:28:26 CET |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, there could be cases when there is only an icon with no text, and for screen readers it is bad because they rely on the text to narrate the content of the page to the user. Consider adding another prop for the ariaLabel, and add it to the button/link directly.
aria-label={ariaLabel}
import { ReactNode } from "react"; | ||
import Link from "next/link"; | ||
|
||
interface IButtonProps { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interface IButtonProps { | |
interface IButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> { |
By doing this we could remove the props from lines 19-22. Consider changing it so we can clean up the code a bit.
const variantMap: Record<"filled" | "outline" | "text", string> = { | ||
filled: `${bgColor} ${textColor} ${borderColor}`, | ||
outline: `bg-transparent ${textColor} ${borderColor}`, | ||
text: `bg-transparent ${textColor} border-transparent`, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you remove these props, remove also the variantMap.
onClick, | ||
className = "", | ||
ariaLabel, | ||
type = "button", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may remove this since you'll have the button props extended into the interface, and it already includes everything.
onClick, | |
className = "", | |
ariaLabel, | |
type = "button", |
variant = "filled", | ||
bgColor = "bg-white", | ||
textColor = "text-black", | ||
borderColor = "transparent", | ||
borderRadius = "rounded", | ||
borderWidth = "border", | ||
padding = "px-4 py-2", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these props are replaceable for the className
onClick?: () => void; | ||
className?: string; | ||
ariaLabel?: string; | ||
type?: "button" | "submit" | "reset"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you follow the precious suggestion, you'll want to remove this.
onClick?: () => void; | |
className?: string; | |
ariaLabel?: string; | |
type?: "button" | "submit" | "reset"; |
sizeMap[size], | ||
variantMap[variant], | ||
padding, | ||
borderRadius, | ||
borderWidth, | ||
className, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sizeMap[size], | |
variantMap[variant], | |
padding, | |
borderRadius, | |
borderWidth, | |
className, | |
sizeMap[size], |
}; | ||
|
||
const baseStyle = [ | ||
"inline-flex items-center justify-center font-medium", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"inline-flex items-center justify-center font-medium", | |
"font-medium rounded-md transition-colors duration-200 cursor-pointer border", |
padding = "px-4 py-2", | ||
icon, | ||
iconPosition = "left", | ||
as = "button", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need that as I will explain later
as = "button", |
const isExternalLink = | ||
href.startsWith("http") || | ||
href.startsWith("mailto:") || | ||
href.startsWith("tel:"); | ||
return ( | ||
<Link | ||
href={href} | ||
className={baseStyle} | ||
aria-label={ariaLabel} | ||
{...(isExternalLink | ||
? { target: "_blank", rel: "noopener noreferrer" } | ||
: {})} | ||
> | ||
{content} | ||
</Link> | ||
); | ||
} | ||
|
||
if (as === "button") { | ||
return ( | ||
<button | ||
type={type} | ||
onClick={onClick} | ||
className={baseStyle} | ||
aria-label={ariaLabel} | ||
> | ||
{content} | ||
</button> | ||
); | ||
} | ||
|
||
return null; | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const isExternalLink = | |
href.startsWith("http") || | |
href.startsWith("mailto:") || | |
href.startsWith("tel:"); | |
return ( | |
<Link | |
href={href} | |
className={baseStyle} | |
aria-label={ariaLabel} | |
{...(isExternalLink | |
? { target: "_blank", rel: "noopener noreferrer" } | |
: {})} | |
> | |
{content} | |
</Link> | |
); | |
} | |
if (as === "button") { | |
return ( | |
<button | |
type={type} | |
onClick={onClick} | |
className={baseStyle} | |
aria-label={ariaLabel} | |
> | |
{content} | |
</button> | |
); | |
} | |
return null; | |
}; | |
if (href) { | |
const isExternalLink = | |
href.startsWith("http") || | |
href.startsWith("mailto:") || | |
href.startsWith("tel:"); | |
return ( | |
<Link | |
href={href} | |
className={baseStyle} | |
aria-label={ariaLabel} | |
{...(isExternalLink | |
? { target: "_blank", rel: "noopener noreferrer" } | |
: {})} | |
> | |
{content} | |
</Link> | |
); | |
} else { | |
return ( | |
<button | |
type={type} | |
onClick={onClick} | |
className={baseStyle} | |
aria-label={ariaLabel} | |
> | |
{content} | |
</button> | |
); | |
} | |
}; |
closes #2
Some usage examples: