diff --git a/src/browser/tests/element/html/dialog.html b/src/browser/tests/element/html/dialog.html
index b4c26df5..e0e6fb45 100644
--- a/src/browser/tests/element/html/dialog.html
+++ b/src/browser/tests/element/html/dialog.html
@@ -76,3 +76,125 @@
testing.expectTrue(dialog instanceof HTMLDialogElement)
}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/browser/webapi/element/html/Dialog.zig b/src/browser/webapi/element/html/Dialog.zig
index c847af21..8bb3616f 100644
--- a/src/browser/webapi/element/html/Dialog.zig
+++ b/src/browser/webapi/element/html/Dialog.zig
@@ -1,6 +1,7 @@
const js = @import("../../../js/js.zig");
const Frame = @import("../../../Frame.zig");
+const Event = @import("../../Event.zig");
const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
@@ -39,6 +40,37 @@ pub fn setReturnValue(self: *Dialog, value: []const u8, frame: *Frame) !void {
try self.asElement().setAttributeSafe(comptime .wrap("returnvalue"), .wrap(value), frame);
}
+/// https://html.spec.whatwg.org/multipage/interactive-elements.html#dom-dialog-show
+/// If the open attribute is set, return; otherwise set it to the empty string.
+/// Focus / inert / top-layer steps are no-ops here — no rendering pipeline.
+pub fn show(self: *Dialog, frame: *Frame) !void {
+ if (self.getOpen()) return;
+ try self.asElement().setAttributeSafe(comptime .wrap("open"), .wrap(""), frame);
+}
+
+/// https://html.spec.whatwg.org/multipage/interactive-elements.html#dom-dialog-showmodal
+/// Throws InvalidStateError if [open] is already set. Sets [open] otherwise.
+/// Focus trap, backdrop, and top-layer placement are no-ops — Lightpanda has
+/// no layout/compositor; [open] reflecting through to selectors is what
+/// downstream consumers rely on.
+pub fn showModal(self: *Dialog, frame: *Frame) !void {
+ if (self.getOpen()) return error.InvalidStateError;
+ try self.asElement().setAttributeSafe(comptime .wrap("open"), .wrap(""), frame);
+}
+
+/// https://html.spec.whatwg.org/multipage/interactive-elements.html#dom-dialog-close
+/// If [open] is unset, return. Otherwise remove [open], optionally update
+/// returnValue, and fire a `close` event (non-bubbling, non-cancelable).
+pub fn close(self: *Dialog, return_value: ?[]const u8, frame: *Frame) !void {
+ if (!self.getOpen()) return;
+ try self.asElement().removeAttribute(comptime .wrap("open"), frame);
+ if (return_value) |v| {
+ try self.asElement().setAttributeSafe(comptime .wrap("returnvalue"), .wrap(v), frame);
+ }
+ const event = try Event.init("close", .{ .bubbles = false, .cancelable = false }, frame._page);
+ try frame._event_manager.dispatch(self.asElement().asEventTarget(), event);
+}
+
pub const JsApi = struct {
pub const bridge = js.Bridge(Dialog);
@@ -50,6 +82,10 @@ pub const JsApi = struct {
pub const open = bridge.accessor(Dialog.getOpen, Dialog.setOpen, .{});
pub const returnValue = bridge.accessor(Dialog.getReturnValue, Dialog.setReturnValue, .{});
+
+ pub const show = bridge.function(Dialog.show, .{});
+ pub const showModal = bridge.function(Dialog.showModal, .{ .dom_exception = true });
+ pub const close = bridge.function(Dialog.close, .{});
};
const testing = @import("../../../../testing.zig");