Button
Displays a button or a component that looks like a button.
Variants
Sizes
States
With Icon
package showcase
import (
"github.com/axzilla/goilerplate/pkg/components"
"github.com/axzilla/goilerplate/pkg/icons"
)
templ ButtonShowcase() {
<div class="flex justify-center items-center border rounded-md py-16 px-4">
<div>
<div class="mb-8">
<h2 class="font-semibold mb-2">Variants</h2>
<div class="flex flex-wrap gap-2">
@components.Button(components.ButtonProps{Text: "Default"})
@components.Button(components.ButtonProps{Text: "Secondary", Variant: components.Secondary})
@components.Button(components.ButtonProps{Text: "Destructive", Variant: components.Destructive})
@components.Button(components.ButtonProps{Text: "Outline", Variant: components.Outline})
@components.Button(components.ButtonProps{Text: "Ghost", Variant: components.Ghost})
@components.Button(components.ButtonProps{Text: "Link", Variant: components.Link})
</div>
</div>
<div class="mb-8">
<h2 class="font-semibold mb-2">Sizes</h2>
<div class="flex flex-wrap items-center gap-2">
@components.Button(components.ButtonProps{Text: "Default"})
@components.Button(components.ButtonProps{Text: "Small", Size: components.Sm})
@components.Button(components.ButtonProps{Text: "Large", Size: components.Lg})
@components.Button(components.ButtonProps{Size: components.ButtonIcon, IconLeft: icons.Rocket(icons.IconProps{Size: "16"})})
</div>
</div>
<div class="mb-8">
<h2 class="font-semibold mb-22">States</h2>
<div class="flex flex-wrap gap-2">
@components.Button(components.ButtonProps{Text: "Default"})
// Alpine.js example
@components.Button(components.ButtonProps{Text: "With Click", Attributes: templ.Attributes{"@click": "alert('Hey Dude!')"}})
// Vanilla JS example
// @components.Button(components.ButtonProps{Text: "With Click", Attributes: templ.Attributes{"onclick": "alert('Hey Dude!')"}})
@components.Button(components.ButtonProps{Text: "Disabled", Disabled: true})
// @components.Button(components.ButtonProps{Text: "Disabled", Disabled: "true"})
@components.Button(components.ButtonProps{Text: "Full Width", Class: "w-full"})
</div>
</div>
<div class="mb-8">
<h2 class="font-semibold mb-2">With Icon</h2>
<div class="flex flex-wrap gap-2">
@components.Button(components.ButtonProps{
Text: "Icon Left",
IconLeft: icons.Rocket(icons.IconProps{Size: "16"}),
})
@components.Button(components.ButtonProps{
Text: "Icon Right",
IconRight: icons.Rocket(icons.IconProps{Size: "16"}),
})
</div>
</div>
</div>
</div>
}
package components
import (
"github.com/axzilla/goilerplate/pkg/utils"
"strings"
)
// ButtonVariant represents the visual style of the button.
type ButtonVariant string
// ButtonSize represents the size of the button.
type ButtonSize string
// Constants for button variants and sizes.
const (
Default ButtonVariant = "default"
Destructive ButtonVariant = "destructive"
Outline ButtonVariant = "outline"
Secondary ButtonVariant = "secondary"
Ghost ButtonVariant = "ghost"
Link ButtonVariant = "link"
Md ButtonSize = "md"
Sm ButtonSize = "sm"
Lg ButtonSize = "lg"
ButtonIcon ButtonSize = "icon"
)
// Button defines the properties for the Button component.
type ButtonProps struct {
// Class specifies additional CSS classes to apply to the button.
// Default: "" (empty string)
Class string
// Text is the content of the button.
// Default: "" (empty string)
Text string
// Variant determines the visual style of the button.
// Default: Default
Variant ButtonVariant
// Size sets the size of the button.
// Default: Md
Size ButtonSize
// FullWidth determines whether the button should take up the full width of its container.
// Default: false
FullWidth bool
// Href, if provided, renders the button as an anchor tag with this URL.
// Default: "" (empty string)
Href string
// Target specifies the target attribute for the anchor tag (only used when Href is provided).
// Default: "" (empty string)
Target string
// Disabled can be either a bool or a string.
// If bool (Go), it directly controls the disabled state.
// If string, it's treated as a JavaScript expression for dynamic disabling.
Disabled any
// Type specifies the type of the button. Default: "button"
// Default: "" (empty string)
Type string
// Attributes allows passing additional HTML attributes to the button or anchor element.
// Default: nil
Attributes templ.Attributes
// IconLeft specifies an icon component to be displayed on the left side of the button text.
// Default: nil
IconLeft templ.Component
// IconRight specifies an icon component to be displayed on the right side of the button text.
// Default: nil
IconRight templ.Component
}
// Variant als Methode
func (b ButtonProps) variantClasses() string {
switch b.Variant {
case Destructive:
return "bg-destructive text-destructive-foreground hover:bg-destructive/90"
case Outline:
return "border border-input bg-background hover:bg-accent hover:text-accent-foreground"
case Secondary:
return "bg-secondary text-secondary-foreground hover:bg-secondary/80"
case Ghost:
return "hover:bg-accent hover:text-accent-foreground"
case Link:
return "text-primary underline-offset-4 hover:underline"
default:
return "bg-primary text-primary-foreground hover:bg-primary/90"
}
}
// Size als Methode
func (b ButtonProps) sizeClasses() string {
switch b.Size {
case Sm:
return "h-9 px-3 rounded-md"
case Lg:
return "h-11 px-8 rounded-md"
case ButtonIcon:
return "h-10 w-10"
default:
return "h-10 px-4 py-2 rounded-md"
}
}
func (b ButtonProps) modifierClasses() string {
classes := []string{}
if b.FullWidth {
classes = append(classes, "w-full")
}
return strings.Join(classes, " ")
}
// Button renders a button or anchor component based on the provided props.
// It can be customized with various visual styles, sizes, and behaviors.
//
// Usage:
//
// @components.Button(components.ButtonProps{
// Text: "Click me",
// Variant: components.Primary,
// Size: components.Md,
// FullWidth: false,
// IconLeft: components.Icon(components.IconProps{Name: "user"}),
// IconRight: components.Icon(components.IconProps{Name: "arrow-right"}),
// Attributes: templ.Attributes{
// "aria-label": "Click this button",
// "data-testid": "main-button",
// },
// })
//
// Props:
// - Class: Additional CSS classes to apply to the button. Default: "" (empty string)
// - Text: The text content of the button. Default: "" (empty string)
// - Variant: The visual style of the button (e.g., Default, Destructive, Outline). Default: Default
// - Size: The size of the button (Md, Sm, Lg, Icon). Default: Md
// - FullWidth: Whether the button should take up the full width of its container. Default: false
// - Href: If provided, renders the button as an anchor tag with this URL. Default: "" (empty string)
// - Target: The target attribute for the anchor tag (only used when Href is provided). Default: "" (empty string)
// - Disabled: Can be either a bool or a string. If bool (Go), it directly controls the disabled state. If string, it's treated as a JavaScript expression for dynamic disabling. Default: nil
// - Type: The type of the button. Default: "button"
// - Attributes: Additional HTML attributes to apply to the button or anchor element. Default: nil
// - IconLeft: An icon component to be displayed on the left side of the button text. Default: nil
// - IconRight: An icon component to be displayed on the right side of the button text. Default: nil
templ Button(props ButtonProps) {
if props.Href != "" {
<a
href={ templ.SafeURL(props.Href) }
target={ props.Target }
class={
utils.TwMerge(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
props.variantClasses(),
props.sizeClasses(),
props.modifierClasses(),
props.Class,
),
}
if props.Disabled != nil {
if disabledBool, ok := props.Disabled.(bool); ok && disabledBool {
aria-disabled="true"
}
if disabledStr, ok := props.Disabled.(string); ok {
:aria-disabled={ disabledStr }
}
}
{ props.Attributes... }
>
{ children... }
@renderButtonContent(props)
</a>
} else {
<button
class={
utils.TwMerge(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors",
"focus-visible:outline-none focus-visible:ring-2 focus-ring-ring focus-visible:ring-offset-2",
"disabled:pointer-events-none disabled:opacity-50",
props.variantClasses(),
props.sizeClasses(),
props.modifierClasses(),
props.Class,
),
}
if props.Type != "" {
type={ props.Type }
}
if props.Disabled != nil {
if disabledBool, ok := props.Disabled.(bool); ok {
disabled?={ disabledBool }
}
if disabledStr, ok := props.Disabled.(string); ok {
:disabled={ disabledStr }
}
}
{ props.Attributes... }
>
{ children... }
@renderButtonContent(props)
</button>
}
}
// renderButtonContent renders the content of the button, including icons and text
templ renderButtonContent(props ButtonProps) {
<span class="flex gap-2 items-center">
if props.IconLeft != nil {
@props.IconLeft
}
{ props.Text }
if props.IconRight != nil {
@props.IconRight
}
</span>
}