Dialog
A modal overlay with a trigger.
<dialog.Dialog id="demo-dialog" title="Edit profile" description="Change your display name.">
<:footer><button.Button>Save</button.Button></:footer>
<:trigger><button.Button variant={button.Outline}>Open dialog</button.Button></:trigger>
Dialog body.
</dialog.Dialog>Installation
Install button and icons first.
Copy each file to its path in the app. Change each import of a registry package to the path of that package in the app.
package dialog
import "github.com/alternayte/gx/registry/icons"
props {
// Id is the id of the dialog element.
// The trigger points at it, so it must be unique on the page.
Id string
// Title is the heading of the dialog. An empty value renders no heading.
Title string = ""
// Description is the text below the title. An empty value renders no description.
Description string = ""
// Trigger is the control that opens the dialog.
Trigger gx.Node = nil
// Footer is the content of the dialog footer, for example the main action. A nil value renders
// no footer.
Footer gx.Node = nil
// Open renders the dialog open. The default is closed.
Open bool = false
// Class adds classes to the dialog element.
Class string = ""
// Children is the content of the dialog body.
Children gx.Node = nil
// Attrs adds HTML attributes to the dialog element.
Attrs gx.Attrs = nil
}
<span class="contents">
if p.Trigger != nil {
<span data-gx-open={"#" + p.Id} class="contents">{p.Trigger}</span>
}
<dialog id={p.Id} open={p.Open} data-gx-dismiss data-gx-trap class={gx.Cx("fixed inset-0 m-auto h-fit w-full max-w-[calc(100%-2rem)] gap-4 rounded-lg border border-border bg-background p-6 text-foreground shadow-lg outline-none open:grid sm:max-w-lg opacity-0 scale-95 transition-[opacity,scale,overlay,display] transition-discrete duration-200 open:opacity-100 open:scale-100 starting:open:opacity-0 starting:open:scale-95 motion-reduce:transition-none backdrop:bg-black/50 backdrop:opacity-0 backdrop:transition-[opacity,overlay,display] backdrop:transition-discrete backdrop:duration-200 open:backdrop:opacity-100 starting:open:backdrop:opacity-0 motion-reduce:backdrop:transition-none", p.Class)} {...p.Attrs}>
if p.Title != "" || p.Description != "" {
<div class="flex flex-col gap-2 text-center sm:text-left">
if p.Title != "" {
<h2 class="text-lg leading-none font-semibold">{p.Title}</h2>
}
if p.Description != "" {
<p class="text-sm text-muted-foreground">{p.Description}</p>
}
</div>
}
if p.Children != nil {
<div class="text-sm">{p.Children}</div>
}
if p.Footer != nil {
<div class="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">{p.Footer}</div>
}
<button type="button" data-gx-close class="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
<icons.X class="size-4" />
<span class="sr-only">Close</span>
</button>
</dialog>
</span>package dialog
import (
"github.com/alternayte/gx"
"github.com/alternayte/gx/registry/button"
)
var DialogFixtures = gx.Fixtures[DialogProps]{
"Default": {
Id: "demo-dialog",
Title: "Edit profile",
Description: "Change your display name.",
Children: gx.Text("Dialog body."),
Footer: button.Button(button.ButtonProps{Children: gx.Text("Save")}),
Trigger: button.Button(button.ButtonProps{Variant: button.Outline, Children: gx.Text("Open dialog")}),
},
}The theme must define these tokens: --background, --foreground, --border, --muted, --muted-foreground, --ring.
Usage
<dialog.Dialog id="edit" title="Edit profile" footer={saveButton}>
<:trigger><button.Button variant={button.Outline}>Open</button.Button></:trigger>
<p>Dialog body.</p>
</dialog.Dialog>Examples
Default
<dialog.Dialog id="demo-dialog" title="Edit profile" description="Change your display name.">
<:footer><button.Button>Save</button.Button></:footer>
<:trigger><button.Button variant={button.Outline}>Open dialog</button.Button></:trigger>
Dialog body.
</dialog.Dialog>API reference
A tag sets a prop by its name with a lower-case first letter: Class is class.
dialog.Dialog
| Prop | Type | Default | Description |
|---|---|---|---|
Id |
string |
Required | Id is the id of the dialog element. The trigger points at it, so it must be unique on the page. |
Title |
string |
"" |
Title is the heading of the dialog. An empty value renders no heading. |
Description |
string |
"" |
Description is the text below the title. An empty value renders no description. |
Trigger |
gx.Node |
nil |
Trigger is the control that opens the dialog. |
Footer |
gx.Node |
nil |
Footer is the content of the dialog footer, for example the main action. A nil value renders no footer. |
Open |
bool |
false |
Open renders the dialog open. The default is closed. |
Class |
string |
"" |
Class adds classes to the dialog element. |
Children |
gx.Node |
nil |
Children is the content of the dialog body. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the dialog element. |
Do and do not
Do
- Give every dialog a unique
Id. - Put the main action in
Footerand a close control in the corner.
Don't
- Do not open a dialog from another dialog.
- Do not put a long form in a dialog. Use a page.
Keyboard
| Key | Action |
|---|---|
| Enter, Space | Opens the dialog from the trigger. |
| Tab | Cycles through the controls of the dialog. |
| Escape | Closes the dialog. |