Sheet
Side-anchored panel that slides in from screen edges.
TailwindCSS
Alpine.js
Sheet Content
This is the main content of the sheet. You can put any components or text here.
Sheet Content
This is the main content of the sheet. You can put any components or text here.
Sheet Content
This is the main content of the sheet. You can put any components or text here.
Sheet Content
This is the main content of the sheet. You can put any components or text here.
package showcase
import "github.com/axzilla/templui/pkg/components"
var sides = []components.SheetSide{
components.SheetSideLeft,
components.SheetSideTop,
components.SheetSideRight,
components.SheetSideBottom,
}
templ Sheet() {
<div class="flex gap-2">
for _, side := range sides {
@components.SheetRoot() {
@components.Sheet(components.SheetProps{
Side: side,
}) {
<div class="py-4">
<h3 class="mb-2 text-lg font-medium">Sheet Content</h3>
<p>This is the main content of the sheet. You can put any components or text here.</p>
</div>
<div class="mt-4">
@components.SheetClose("Close")
</div>
}
@components.SheetTrigger(string(side), side) {
@components.Button(components.ButtonProps{Text: string(side)})
}
}
}
</div>
}
package components
type SheetSide string
const (
SheetSideTop SheetSide = "top"
SheetSideRight SheetSide = "right"
SheetSideBottom SheetSide = "bottom"
SheetSideLeft SheetSide = "left"
)
type SheetProps struct {
Title string // Header text
Description string // Subheading text
Side SheetSide // Slide-in direction
}
templ SheetScript() {
{{ handle := templ.NewOnceHandle() }}
@handle.Once() {
<script nonce={ templ.GetNonce(ctx) }>
document.addEventListener('alpine:init', () => {
Alpine.data('sheet', () => ({
isOpen: false,
open() {
this.isOpen = true
},
close() {
this.isOpen = false
},
}))
})
</script>
}
}
// SheetRoot initializes Alpine.js state and event handlers
// Must wrap Sheet components and triggers
templ SheetRoot() {
<div
x-data="sheet"
@keydown.escape.window="close"
>
{ children... }
</div>
}
// Side-anchored panel that slides in from screen edges.
templ Sheet(props SheetProps) {
<!-- Backdrop -->
<div
x-show="isOpen"
class="fixed inset-0 bg-background/80 backdrop-blur-sm"
@click="close"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
></div>
<!-- Sheet -->
<div
x-show="isOpen"
class={
"z-50",
templ.KV("fixed inset-y-0 right-0 w-3/4 md:w-1/2 lg:w-1/3", props.Side == SheetSideRight),
templ.KV("fixed inset-y-0 left-0 w-3/4 md:w-1/2 lg:w-1/3", props.Side == SheetSideLeft),
templ.KV("fixed inset-x-0 top-0 h-auto sm:h-1/2", props.Side == SheetSideTop),
templ.KV("fixed inset-x-0 bottom-0 h-auto sm:h-1/2", props.Side == SheetSideBottom),
}
x-transition:enter="transition ease-out duration-300"
x-transition:leave="transition ease-in duration-300"
if props.Side == SheetSideLeft {
x-transition:enter-start="opacity-0 transform -translate-x-full"
x-transition:enter-end="opacity-100 transform translate-x-0"
x-transition:leave-start="opacity-100 transform translate-x-0"
x-transition:leave-end="opacity-0 transform -translate-x-full"
}
if props.Side == SheetSideRight {
x-transition:enter-start="opacity-0 transform translate-x-full"
x-transition:enter-end="opacity-100 transform translate-x-0"
x-transition:leave-start="opacity-100 transform translate-x-0"
x-transition:leave-end="opacity-0 transform translate-x-full"
}
if props.Side == SheetSideTop {
x-transition:enter-start="opacity-0 transform -translate-y-full"
x-transition:enter-end="opacity-100 transform translate-y-0"
x-transition:leave-start="opacity-100 transform translate-y-0"
x-transition:leave-end="opacity-0 transform -translate-y-full"
}
if props.Side == SheetSideBottom {
x-transition:enter-start="opacity-0 transform translate-y-full"
x-transition:enter-end="opacity-100 transform translate-y-0"
x-transition:leave-start="opacity-100 transform translate-y-0"
x-transition:leave-end="opacity-0 transform translate-y-full"
}
>
<div
class={ "h-full overflow-y-auto bg-background p-6 shadow-lg",
templ.KV("border-l", props.Side == SheetSideRight),
templ.KV("border-r", props.Side == SheetSideLeft),
templ.KV("border-t", props.Side == SheetSideBottom),
templ.KV("border-b", props.Side == SheetSideTop) }
>
<div class="flex flex-col space-y-2">
<h2 class="text-lg font-semibold">{ props.Title }</h2>
<p class="text-sm text-muted-foreground">{ props.Description }</p>
</div>
<div class="mt-4">
{ children... }
</div>
</div>
</div>
}
// SheetTrigger creates elements that open the sheet
// Must be used within SheetRoot
templ SheetTrigger(text string, side SheetSide) {
<span @click="open">
{ children... }
</span>
}
// SheetClose creates a button that closes the sheet
// Must be used within Sheet
templ SheetClose(text string) {
<button
@click="close"
class={ "inline-flex items-center justify-center 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",
"disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent",
"hover:text-accent-foreground h-10 px-4 py-2" }
>
{ text }
</button>
}
Usage
1. Add the script to your page/layout:
// Option A: All components (recommended)
@utils.ComponentScripts()
// Option B: Just Sheet
@components.SheetScript()
2. Use the component:
@components.Sheet(components.SheetProps{...})