From f0d703ff408cc385de23023d9577110e549d8f58 Mon Sep 17 00:00:00 2001 From: Simon Leeb <52261246+sliemeobn@users.noreply.github.com> Date: Fri, 13 Feb 2026 22:23:04 +0100 Subject: [PATCH 1/2] added `Group` type --- Sources/Elementary/Core/Group.swift | 42 +++++++++++++++++++ Tests/ElementaryTests/TagRenderingTests.swift | 18 ++++++++ 2 files changed, 60 insertions(+) create mode 100644 Sources/Elementary/Core/Group.swift diff --git a/Sources/Elementary/Core/Group.swift b/Sources/Elementary/Core/Group.swift new file mode 100644 index 0000000..a255e99 --- /dev/null +++ b/Sources/Elementary/Core/Group.swift @@ -0,0 +1,42 @@ +/// A container that groups HTML content without introducing any additional HTML tags. +/// +/// This type is useful when you want to return multiple sibling nodes from a single `some HTML` +/// context without adding an extra wrapper element. +/// +/// - Note: In Embedded mode, the HTML builder currently supports up to **6 direct child views** +/// in a single block (because variadic generics aren't available there yet). You can exceed +/// that limit by nesting content inside one or more `Group` blocks. +public struct Group: HTML { + public typealias Body = Never + public typealias Tag = Never + + public let content: Content + + @inlinable + public init(@HTMLBuilder content: () -> Content) { + self.content = content() + } + + @inlinable + public static func _render( + _ html: consuming Self, + into renderer: inout Renderer, + with context: consuming _RenderingContext + ) { + context.assertNoAttributes(self) + Content._render(html.content, into: &renderer, with: context) + } + + @inlinable + @_unavailableInEmbedded + public static func _render( + _ html: consuming Self, + into renderer: inout Renderer, + with context: consuming _RenderingContext + ) async throws { + context.assertNoAttributes(self) + try await Content._render(html.content, into: &renderer, with: context) + } +} + +extension Group: Sendable where Content: Sendable {} diff --git a/Tests/ElementaryTests/TagRenderingTests.swift b/Tests/ElementaryTests/TagRenderingTests.swift index 6e11a15..1070eca 100644 --- a/Tests/ElementaryTests/TagRenderingTests.swift +++ b/Tests/ElementaryTests/TagRenderingTests.swift @@ -33,6 +33,24 @@ final class TagRenderingTests: XCTestCase { ) } + func testRendersGroup() async throws { + try await HTMLAssertEqual( + div { + Group { + h1 {} + Group { + if true { + p {} + p {} + } + } + h1 {} + } + }, + "

" + ) + } + func testRendersOptionals() async throws { try await HTMLAssertEqual( div { From 2db17680f9de88221077638a5cc06ca40a00cda5 Mon Sep 17 00:00:00 2001 From: Simon Leeb <52261246+sliemeobn@users.noreply.github.com> Date: Fri, 13 Feb 2026 22:29:10 +0100 Subject: [PATCH 2/2] type mess with deprecated Content --- Sources/Elementary/Core/Group.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Elementary/Core/Group.swift b/Sources/Elementary/Core/Group.swift index a255e99..8c5e3bb 100644 --- a/Sources/Elementary/Core/Group.swift +++ b/Sources/Elementary/Core/Group.swift @@ -7,8 +7,9 @@ /// in a single block (because variadic generics aren't available there yet). You can exceed /// that limit by nesting content inside one or more `Group` blocks. public struct Group: HTML { - public typealias Body = Never public typealias Tag = Never + public typealias Body = Never + public typealias Content = Content public let content: Content