Card
Container for organizing related content and
TailwindCSS
Create Project
Deploy your new project in one-click.
PostgreSQL
MySQL
SQLite
package showcase
import (
"github.com/axzilla/templui/internal/components/button"
"github.com/axzilla/templui/internal/components/card"
"github.com/axzilla/templui/internal/components/input"
"github.com/axzilla/templui/internal/components/label"
"github.com/axzilla/templui/internal/components/selectbox"
)
templ CardDefault() {
<div class="w-full max-w-sm">
@card.Card() {
@card.Header() {
@card.Title() {
Create Project
}
@card.Description() {
Deploy your new project in one-click.
}
}
@card.Content() {
<div class="flex flex-col gap-4">
<div class="w-full max-w-sm grid gap-2">
@label.Label(label.Props{
For: "name",
}) {
Name
}
@input.Input(input.Props{
ID: "name",
Placeholder: "Enter project name",
})
</div>
<div class="w-full max-w-sm grid gap-2">
@label.Label(label.Props{
For: "service",
}) {
Service
}
@selectbox.SelectBox() {
@selectbox.Trigger(selectbox.TriggerProps{
ID: "service",
}) {
@selectbox.Value(selectbox.ValueProps{
Placeholder: "Select",
})
}
@selectbox.Content() {
@selectbox.Group() {
@selectbox.Item(selectbox.ItemProps{
Value: "postgres",
}) {
PostgreSQL
}
@selectbox.Item(selectbox.ItemProps{
Value: "mysql",
}) {
MySQL
}
@selectbox.Item(selectbox.ItemProps{
Value: "sqlite",
}) {
SQLite
}
}
}
}
</div>
</div>
}
@card.Footer(card.FooterProps{
Class: "flex justify-between",
}) {
@button.Button(button.Props{
Variant: button.VariantSecondary,
}) {
Cancel
}
@button.Button() {
Deploy
}
}
}
</div>
}
Installation
templui add card
Copy and paste the following code into your project:
package card import ( "github.com/axzilla/templui/internal/components/aspectratio" "github.com/axzilla/templui/internal/utils" ) type MediaPosition string type MediaWidth string const ( MediaPositionTop MediaPosition = "top" MediaPositionBottom MediaPosition = "bottom" MediaPositionLeft MediaPosition = "left" MediaPositionRight MediaPosition = "right" ) const ( MediaWidthAuto MediaWidth = "auto" MediaWidthFull MediaWidth = "full" MediaWidthHalf MediaWidth = "half" MediaWidthThird MediaWidth = "third" MediaWidthQuarter MediaWidth = "quarter" MediaWidthTwoThirds MediaWidth = "two-thirds" MediaWidthThreeQuarters MediaWidth = "three-quarters" ) type Props struct { ID string Class string Attributes templ.Attributes } type HeaderProps struct { ID string Class string Attributes templ.Attributes } type TitleProps struct { ID string Class string Attributes templ.Attributes } type DescriptionProps struct { ID string Class string Attributes templ.Attributes } type ContentProps struct { ID string Class string Attributes templ.Attributes } type FooterProps struct { ID string Class string Attributes templ.Attributes } type HorizontalProps struct { ID string Class string Attributes templ.Attributes } type MediaProps struct { ID string Class string Attributes templ.Attributes Src string Alt string Position MediaPosition Width MediaWidth AspectRatio aspectratio.Ratio } templ Card(props ...Props) { {{ var p Props }} if len(props) > 0 { {{ p = props[0] }} } <div if p.ID != "" { id={ p.ID } } class={ utils.TwMerge( "w-full rounded-lg border bg-card text-card-foreground shadow-xs", p.Class, ), } { p.Attributes... } > { children... } </div> } templ Header(props ...HeaderProps) { {{ var p HeaderProps }} if len(props) > 0 { {{ p = props[0] }} } <div if p.ID != "" { id={ p.ID } } class={ utils.TwMerge( "flex flex-col space-y-1.5 p-6 pb-0", p.Class, ), } { p.Attributes... } > { children... } </div> } templ Title(props ...TitleProps) { {{ var p TitleProps }} if len(props) > 0 { {{ p = props[0] }} } <h3 if p.ID != "" { id={ p.ID } } class={ utils.TwMerge( "text-lg font-semibold leading-none tracking-tight", p.Class, ), } { p.Attributes... } > { children... } </h3> } templ Description(props ...DescriptionProps) { {{ var p DescriptionProps }} if len(props) > 0 { {{ p = props[0] }} } <p if p.ID != "" { id={ p.ID } } class={ utils.TwMerge( "text-sm text-muted-foreground", p.Class, ), } { p.Attributes... } > { children... } </p> } templ Content(props ...ContentProps) { {{ var p ContentProps }} if len(props) > 0 { {{ p = props[0] }} } <div if p.ID != "" { id={ p.ID } } class={ utils.TwMerge( "p-6", p.Class, ), } { p.Attributes... } > { children... } </div> } templ Footer(props ...FooterProps) { {{ var p FooterProps }} if len(props) > 0 { {{ p = props[0] }} } <div if p.ID != "" { id={ p.ID } } class={ utils.TwMerge( "flex items-center p-6 pt-0", p.Class, ), } { p.Attributes... } > { children... } </div> } templ Horizontal(props ...HorizontalProps) { {{ var p HorizontalProps }} if len(props) > 0 { {{ p = props[0] }} } <div if p.ID != "" { id={ p.ID } } class={ utils.TwMerge( "flex overflow-hidden", p.Class, ), } { p.Attributes... } > { children... } </div> } templ Media(props ...MediaProps) { {{ var p MediaProps }} if len(props) > 0 { {{ p = props[0] }} } <div if p.ID != "" { id={ p.ID } } class={ utils.TwMerge( "overflow-hidden", mediaPositionClasses(p.Position, p.Width), p.Class, ), } { p.Attributes... } > @aspectratio.AspectRatio(aspectratio.Props{ ID: p.ID + "-aspect", Ratio: p.AspectRatio, Class: "h-full w-full", }) { <img if p.Src != "" { src={ p.Src } } if p.Alt != "" { alt={ p.Alt } } class="h-full w-full object-cover" /> } </div> } func mediaPositionClasses(position MediaPosition, width MediaWidth) string { var positionClass string switch position { case MediaPositionTop: return "w-full rounded-t-lg" case MediaPositionBottom: return "w-full rounded-b-lg" case MediaPositionLeft: positionClass = "shrink-0 rounded-l-lg" case MediaPositionRight: positionClass = "shrink-0 rounded-r-lg" default: positionClass = "" } if position == MediaPositionLeft || position == MediaPositionRight { return positionClass + " " + widthClass(width) } return positionClass } func widthClass(width MediaWidth) string { switch width { case MediaWidthFull: return "w-full" case MediaWidthHalf: return "w-1/2" case MediaWidthThird: return "w-1/3" case MediaWidthQuarter: return "w-1/4" case MediaWidthTwoThirds: return "w-2/3" case MediaWidthThreeQuarters: return "w-3/4" default: return "w-1/3" } }
Update the import paths to match your project setup.
Examples
Image Left

Side Image Card
With left-aligned image
This card demonstrates the left image layout.
package showcase
import (
"github.com/axzilla/templui/internal/components/aspectratio"
"github.com/axzilla/templui/internal/components/card"
)
templ CardImageLeft() {
<div class="w-full max-w-sm">
@card.Card() {
@card.Horizontal() {
@card.Media(card.MediaProps{
ID: "left-media",
Alt: "Left side image",
Position: card.MediaPositionLeft,
Width: card.MediaWidthThird,
AspectRatio: aspectratio.RatioAuto,
Src: "/assets/img/card_placeholder.jpeg",
},
)
<div class="flex flex-col flex-1">
@card.Header() {
@card.Title() {
Side Image Card
}
@card.Description() {
With left-aligned image
}
}
@card.Content() {
<p>This card demonstrates the left image layout.</p>
}
</div>
}
}
</div>
}
Image Right
Side Image Card
With right-aligned image
This card demonstrates the right image layout.

package showcase
import (
"github.com/axzilla/templui/internal/components/aspectratio"
"github.com/axzilla/templui/internal/components/card"
)
templ CardImageRight() {
<div class="w-full max-w-sm">
@card.Card() {
@card.Horizontal() {
<div class="flex flex-col flex-1">
@card.Header() {
@card.Title() {
Side Image Card
}
@card.Description() {
With right-aligned image
}
}
@card.Content() {
<p>This card demonstrates the right image layout.</p>
}
</div>
@card.Media(card.MediaProps{
ID: "right-media",
Alt: "Right side image",
Position: card.MediaPositionRight,
Width: card.MediaWidthThird,
AspectRatio: aspectratio.RatioAuto,
Src: "/assets/img/card_placeholder.jpeg",
},
)
}
}
</div>
}
Image Top

Featured Card
With top image
This card shows top image usage.
package showcase
import (
"github.com/axzilla/templui/internal/components/aspectratio"
"github.com/axzilla/templui/internal/components/button"
"github.com/axzilla/templui/internal/components/card"
)
templ CardImageTop() {
<div class="w-full max-w-sm">
@card.Card() {
@card.Media(card.MediaProps{
ID: "top-media",
Alt: "Card image",
Position: card.MediaPositionTop,
AspectRatio: aspectratio.RatioVideo,
Src: "/assets/img/card_placeholder.jpeg",
},
)
@card.Header() {
@card.Title() {
Featured Card
}
@card.Description() {
With top image
}
}
@card.Content() {
<p>This card shows top image usage.</p>
}
@card.Footer() {
@button.Button() {
Learn more
}
}
}
</div>
}
Image Bottom
Featured Card
With bottom image
This card shows bottom image usage.

package showcase
import (
"github.com/axzilla/templui/internal/components/aspectratio"
"github.com/axzilla/templui/internal/components/button"
"github.com/axzilla/templui/internal/components/card"
)
templ CardImageBottom() {
<div class="w-full max-w-sm">
@card.Card() {
@card.Header() {
@card.Title() {
Featured Card
}
@card.Description() {
With bottom image
}
}
@card.Content() {
<p>This card shows bottom image usage.</p>
}
@card.Footer() {
@button.Button() {
Learn more
}
}
@card.Media(card.MediaProps{
ID: "bottom-media",
Alt: "Card image",
Position: card.MediaPositionBottom,
AspectRatio: aspectratio.RatioVideo,
Src: "/assets/img/card_placeholder.jpeg",
},
)
}
</div>
}