Boundary#
Component <neo-boundary> is a logical scoping region for overlays. An overlay inside it (Popover, Menu, Select, Combobox, or Text input suggestions) treats the boundary as its local document and viewport. Its scope attribute is a space-separated token list that defaults to "dismiss positioning". By default, outside clicks and focus do not dismiss the overlay, and its panel clamps to the region instead of the viewport. Setting scope replaces that default. Add a token to opt into a scope or drop one to turn it off. The scroll token lets independent scrollers outside the region move without closing the overlay; stacking layers the region's overlays inside it rather than the document root. Both are off by default. The element is layout-neutral (display: contents) until stacking gives it a box, and never clips a panel. Escape and trigger toggles always close an overlay.
Reference#
Attributes #
| Name | Type | Default | Description |
|---|---|---|---|
scope | "dismiss" | "positioning" | "scroll" | "stacking" (space-separated) | "dismiss positioning" | Space-separated list of the scopes this region applies. Setting it replaces the default (dismiss positioning) entirely. List every scope you want; omit a token to fall back to the document / viewport default for it, and scope="" scopes nothing. With dismiss, a pointer or focus event outside the boundary does not close an overlay inside it (only events inside the boundary but outside the overlay do). With positioning, overlay panels clamp and flip against this region's box (the union of the boundary's laid-out descendant rects) instead of the viewport. With scroll, an independent scroll container outside this region can move without closing an embedded overlay; a scroll inside the region, or on an ancestor scroller that carries the region, still dismisses. With stacking, the region establishes a local stacking context (isolation: isolate) so its overlays layer inside it, not the document root; this is the one scope that drops display: contents for a box. |
Slots #
| Name | Description |
|---|---|
| default | The content the boundary scopes. Each overlay inside reads its nearest <neo-boundary> ancestor (via closest("neo-boundary")) and obeys that boundary's scopes only. Rules are not inherited. A closer boundary that turns a scope off falls back to the document or viewport default for that scope, not to an outer boundary. Scoping is purely logical (composed-path containment) and non-visual. A fixed or top-layer panel still counts as inside through its host even though it paints outside the boundary's box. |
Examples#
Scoped dismiss#
2 regions, each a <neo-boundary>. Open an overlay in one, then click or open something in the other. The first stays open because each boundary scopes dismiss to itself. Without boundaries, the second interaction would close the first.
Actions
Rename, archive, or delete.
Actions
Rename, archive, or delete.
<neo-layout row wrap align-items="stretch" gap="lg">
<neo-boundary>
<div
neo-flex-grow="1"
neo-flex-basis="0"
class="boundary-scoped-dismiss-panel"
>
<strong>Project A</strong>
<neo-popover
placement="bottom-start"
follow-scroll="until-trigger-invisible"
>
<neo-button data-neo-popover-trigger>Actions</neo-button>
<div data-neo-popover-content>
<h3 class="boundary-scoped-dismiss-heading">Actions</h3>
<p class="boundary-scoped-dismiss-note">Rename, archive, or delete.</p>
</div>
</neo-popover>
<neo-select
placeholder="Pick a status"
aria-label="Status (Project A)"
follow-scroll="until-trigger-invisible"
class="boundary-scoped-dismiss-field"
>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="todo"
data-neo-value="todo"
>To do</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="doing"
data-neo-value="doing"
>In progress</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="done"
data-neo-value="done"
>Done</neo-option>
</neo-select>
<neo-combobox
placeholder="Pick an assignee"
search-placeholder="Search…"
aria-label="Assignee (Project A)"
follow-scroll="until-trigger-invisible"
class="boundary-scoped-dismiss-field"
>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="alice"
data-neo-value="alice"
>Alice Larsson</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="diego"
data-neo-value="diego"
>Diego Vega</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="evelyn"
data-neo-value="evelyn"
>Evelyn Kone</neo-option>
</neo-combobox>
</div>
</neo-boundary>
<neo-boundary>
<div
neo-flex-grow="1"
neo-flex-basis="0"
class="boundary-scoped-dismiss-panel"
>
<strong>Project B</strong>
<neo-popover
placement="bottom-start"
follow-scroll="until-trigger-invisible"
>
<neo-button data-neo-popover-trigger>Actions</neo-button>
<div data-neo-popover-content>
<h3 class="boundary-scoped-dismiss-heading">Actions</h3>
<p class="boundary-scoped-dismiss-note">Rename, archive, or delete.</p>
</div>
</neo-popover>
<neo-select
placeholder="Pick a status"
aria-label="Status (Project B)"
follow-scroll="until-trigger-invisible"
class="boundary-scoped-dismiss-field"
>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="backlog"
data-neo-value="backlog"
>Backlog</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="active"
data-neo-value="active"
>Active</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="shipped"
data-neo-value="shipped"
>Shipped</neo-option>
</neo-select>
<neo-combobox
placeholder="Pick an assignee"
search-placeholder="Search…"
aria-label="Assignee (Project B)"
follow-scroll="until-trigger-invisible"
class="boundary-scoped-dismiss-field"
>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="samira"
data-neo-value="samira"
>Samira Khalil</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="theo"
data-neo-value="theo"
>Theo Becker</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="jan"
data-neo-value="jan"
>Jan O'Sullivan</neo-option>
</neo-combobox>
</div>
</neo-boundary>
</neo-layout>package examples
import "github.com/romshark/morpheus/neo"
templ BoundaryScopedDismiss() {
@neo.Layout(neo.LayoutOpts{
Direction: neo.Set(neo.Row),
Wrap: neo.Set(neo.Wrap),
Gap: neo.Set(neo.GapLg),
AlignItems: neo.Set(neo.AlignItemsStretch),
}) {
@neo.Boundary(neo.BoundaryOpts{}) {
<div neo-flex-grow="1" neo-flex-basis="0" class="boundary-scoped-dismiss-panel">
<strong>Project A</strong>
@neo.Popover(neo.PopoverOpts{
Placement: neo.Set(neo.PlacementBottomStart),
FollowScroll: neo.Set(neo.PopoverFollowScrollUntilTriggerInvisible),
}) {
<neo-button { neo.PopoverTrigger()... }>Actions</neo-button>
<div { neo.PopoverContent()... }>
<h3 class="boundary-scoped-dismiss-heading">Actions</h3>
<p class="boundary-scoped-dismiss-note">Rename, archive, or delete.</p>
</div>
}
@neo.SelectAttrs(neo.SelectOpts{
AriaLabel: neo.Set("Status (Project A)"),
Placeholder: neo.Set("Pick a status"),
}, templ.Attributes{"class": "boundary-scoped-dismiss-field"}) {
@neo.Option(neo.OptionOpts{Value: neo.Set("todo")}) {
To do
}
@neo.Option(neo.OptionOpts{Value: neo.Set("doing")}) {
In progress
}
@neo.Option(neo.OptionOpts{Value: neo.Set("done")}) {
Done
}
}
@neo.ComboboxAttrs(neo.ComboboxOpts{
AriaLabel: neo.Set("Assignee (Project A)"),
Placeholder: neo.Set("Pick an assignee"),
SearchPlaceholder: neo.Set("Search…"),
}, templ.Attributes{"class": "boundary-scoped-dismiss-field"}) {
@neo.Option(neo.OptionOpts{Value: neo.Set("alice")}) {
Alice Larsson
}
@neo.Option(neo.OptionOpts{Value: neo.Set("diego")}) {
Diego Vega
}
@neo.Option(neo.OptionOpts{Value: neo.Set("evelyn")}) {
Evelyn Kone
}
}
</div>
}
@neo.Boundary(neo.BoundaryOpts{}) {
<div neo-flex-grow="1" neo-flex-basis="0" class="boundary-scoped-dismiss-panel">
<strong>Project B</strong>
@neo.Popover(neo.PopoverOpts{
Placement: neo.Set(neo.PlacementBottomStart),
FollowScroll: neo.Set(neo.PopoverFollowScrollUntilTriggerInvisible),
}) {
<neo-button { neo.PopoverTrigger()... }>Actions</neo-button>
<div { neo.PopoverContent()... }>
<h3 class="boundary-scoped-dismiss-heading">Actions</h3>
<p class="boundary-scoped-dismiss-note">Rename, archive, or delete.</p>
</div>
}
@neo.SelectAttrs(neo.SelectOpts{
AriaLabel: neo.Set("Status (Project B)"),
Placeholder: neo.Set("Pick a status"),
}, templ.Attributes{"class": "boundary-scoped-dismiss-field"}) {
@neo.Option(neo.OptionOpts{Value: neo.Set("backlog")}) {
Backlog
}
@neo.Option(neo.OptionOpts{Value: neo.Set("active")}) {
Active
}
@neo.Option(neo.OptionOpts{Value: neo.Set("shipped")}) {
Shipped
}
}
@neo.ComboboxAttrs(neo.ComboboxOpts{
AriaLabel: neo.Set("Assignee (Project B)"),
Placeholder: neo.Set("Pick an assignee"),
SearchPlaceholder: neo.Set("Search…"),
}, templ.Attributes{"class": "boundary-scoped-dismiss-field"}) {
@neo.Option(neo.OptionOpts{Value: neo.Set("samira")}) {
Samira Khalil
}
@neo.Option(neo.OptionOpts{Value: neo.Set("theo")}) {
Theo Becker
}
@neo.Option(neo.OptionOpts{Value: neo.Set("jan")}) {
Jan O'Sullivan
}
}
</div>
}
}
}
.boundary-scoped-dismiss-panel {
min-width: 15rem;
padding: 1.25rem;
border: 1px dashed var(--muted);
border-radius: var(--page-radius);
display: flex;
flex-direction: column;
gap: 0.75rem;
align-items: flex-start;
}
.boundary-scoped-dismiss-heading {
margin: 0 0 0.25rem;
}
.boundary-scoped-dismiss-note {
margin: 0;
color: var(--muted);
}
.boundary-scoped-dismiss-field {
width: 12rem;
}
Positioning containment#
Open the popover. The panel clamps and flips against the dashed region instead of the viewport. The trigger sits at the region's bottom edge, and the panel flips above it to stay inside. Set scope="dismiss" to clamp to the viewport instead.
Filters
The panel is clamped to the dashed region instead of the viewport. It flips above the trigger to stay inside. Drop the positioning token from scope to clamp to the viewport instead.
<neo-boundary>
<div class="boundary-positioning-region">
<neo-popover placement="bottom" follow-scroll="until-trigger-invisible">
<neo-button data-neo-popover-trigger>Filters</neo-button>
<div data-neo-popover-content>
<h3 class="boundary-positioning-heading">Filters</h3>
<p class="boundary-positioning-body">The panel is clamped to the dashed region instead of the viewport. It flips above the trigger to stay inside. Drop the <code>positioning</code> token from <code>scope</code> to clamp to the viewport instead.</p>
</div>
</neo-popover>
</div>
</neo-boundary>package examples
import "github.com/romshark/morpheus/neo"
templ BoundaryPositioning() {
@neo.Boundary(neo.BoundaryOpts{}) {
<div class="boundary-positioning-region">
@neo.Popover(neo.PopoverOpts{
Placement: neo.Set(neo.PlacementBottom),
FollowScroll: neo.Set(neo.PopoverFollowScrollUntilTriggerInvisible),
}) {
<neo-button { neo.PopoverTrigger()... }>Filters</neo-button>
<div { neo.PopoverContent()... }>
<h3 class="boundary-positioning-heading">Filters</h3>
<p class="boundary-positioning-body">
The panel is clamped to the dashed region instead of the viewport. It
flips above the trigger to stay inside. Drop the <code>positioning</code>
token from <code>scope</code> to clamp to the viewport instead.
</p>
</div>
}
</div>
}
}
.boundary-positioning-region {
height: 13rem;
padding: 1rem;
border: 1px dashed var(--muted);
border-radius: var(--page-radius);
display: flex;
align-items: flex-end;
justify-content: center;
}
.boundary-positioning-heading {
margin: 0 0 0.25rem;
}
.boundary-positioning-body {
margin: 0;
}
Scroll scope#
The Account menu is an embedded widget inside a scrollable panel; its boundary adds the scroll token (scope="dismiss positioning scroll"). Open the menu, then scroll the panel around it. Because that scroller carries the boundary itself, the popover closes instead of floating through the container. Independent scrollers outside the boundary can move without dismissing it.
Diego Vega commented on Onboarding flow.
Evelyn Kone merged Release 2.4.
Account
Signed in as Alice Larsson.
An embedded widget, scoped by its own <neo-boundary>.
Samira Khalil opened Billing audit.
Theo Becker closed Flaky e2e test.
Jan O'Sullivan updated Design tokens.
Alice Larsson archived Q3 planning.
Maya Lindqvist pinned the Roadmap.
<div class="boundary-scroll">
<p class="boundary-scroll-line">Diego Vega commented on Onboarding flow.</p>
<p class="boundary-scroll-line">Evelyn Kone merged Release 2.4.</p>
<neo-boundary scope="dismiss positioning scroll">
<div class="boundary-scroll-widget">
<neo-popover placement="bottom-start">
<neo-button data-neo-popover-trigger>Account</neo-button>
<div data-neo-popover-content>
<h3 class="boundary-scroll-heading">Account</h3>
<p class="boundary-scroll-line">Signed in as Alice Larsson.</p>
</div>
</neo-popover>
<p class="boundary-scroll-line">An embedded widget, scoped by its own <code><neo-boundary></code>.</p>
</div>
</neo-boundary>
<p class="boundary-scroll-line">Samira Khalil opened Billing audit.</p>
<p class="boundary-scroll-line">Theo Becker closed Flaky e2e test.</p>
<p class="boundary-scroll-line">Jan O'Sullivan updated Design tokens.</p>
<p class="boundary-scroll-line">Alice Larsson archived Q3 planning.</p>
<p class="boundary-scroll-line">Maya Lindqvist pinned the Roadmap.</p>
</div>package examples
import "github.com/romshark/morpheus/neo"
templ BoundaryScroll() {
<div class="boundary-scroll">
<p class="boundary-scroll-line">Diego Vega commented on Onboarding flow.</p>
<p class="boundary-scroll-line">Evelyn Kone merged Release 2.4.</p>
@neo.Boundary(neo.BoundaryOpts{Scopes: neo.Set(neo.BoundaryScopes{Scroll: true})}) {
<div class="boundary-scroll-widget">
@neo.Popover(neo.PopoverOpts{Placement: neo.Set(neo.PlacementBottomStart)}) {
<neo-button { neo.PopoverTrigger()... }>Account</neo-button>
<div { neo.PopoverContent()... }>
<h3 class="boundary-scroll-heading">Account</h3>
<p class="boundary-scroll-line">Signed in as Alice Larsson.</p>
</div>
}
<p class="boundary-scroll-line">An embedded widget, scoped by its own <code>{ "<neo-boundary>" }</code>.</p>
</div>
}
<p class="boundary-scroll-line">Samira Khalil opened Billing audit.</p>
<p class="boundary-scroll-line">Theo Becker closed Flaky e2e test.</p>
<p class="boundary-scroll-line">Jan O'Sullivan updated Design tokens.</p>
<p class="boundary-scroll-line">Alice Larsson archived Q3 planning.</p>
<p class="boundary-scroll-line">Maya Lindqvist pinned the Roadmap.</p>
</div>
}
.boundary-scroll {
height: 14rem;
overflow-y: auto;
padding: 1.25rem;
border: 1px solid var(--muted);
border-radius: var(--page-radius);
display: flex;
flex-direction: column;
gap: 1rem;
}
.boundary-scroll-line {
margin: 0;
color: var(--muted);
}
.boundary-scroll-widget {
padding: 1rem;
border: 1px dashed var(--muted);
border-radius: var(--page-radius);
display: flex;
flex-direction: column;
gap: 0.6rem;
align-items: flex-start;
}
.boundary-scroll-heading {
margin: 0 0 0.25rem;
}
Stacking scope#
Stacking scope is opt-in. The right widget adds the stacking token to establish a local stacking context, which layers its overlays inside the region instead of the document root. Both widgets open the same popover (global z-index: 1000) across a host overlay at z-index: 100.
- Left (default): no stacking context. The panel outranks the host overlay and paints over it.
- Right (
stacking): the panel is confined to the region's context, and the host overlay paints over it instead.
Open both popovers to compare. Enabling stacking gives the region a box (isolation: isolate), the one scope that drops display: contents.
Default
Account
Signed in as Alice Larsson.
Plan: Team · 4 seats
No stacking context. The panel's z-index 1000 paints over the host overlay.
stacking
Account
Signed in as Alice Larsson.
Plan: Team · 4 seats
The host overlay paints over the panel because a local stacking context confines it.
<div class="boundary-stacking">
<div class="boundary-stacking-overlay">Host app overlay · z-index 100</div>
<div class="boundary-stacking-row">
<div class="boundary-stacking-col">
<neo-boundary>
<div class="boundary-stacking-panel">
<p class="boundary-stacking-label">Default</p>
<neo-popover placement="bottom-start">
<neo-button data-neo-popover-trigger>Account</neo-button>
<div data-neo-popover-content>
<h3 class="boundary-stacking-heading">Account</h3>
<p class="boundary-stacking-line">Signed in as Alice Larsson.</p>
<p class="boundary-stacking-line-sm">Plan: Team · 4 seats</p>
<p class="boundary-stacking-line-muted">No stacking context. The panel's z-index 1000 paints over the host overlay.</p>
</div>
</neo-popover>
</div>
</neo-boundary>
</div>
<div class="boundary-stacking-col">
<neo-boundary scope="dismiss positioning stacking">
<div class="boundary-stacking-panel">
<p class="boundary-stacking-label">stacking</p>
<neo-popover placement="bottom-start">
<neo-button data-neo-popover-trigger>Account</neo-button>
<div data-neo-popover-content>
<h3 class="boundary-stacking-heading">Account</h3>
<p class="boundary-stacking-line">Signed in as Alice Larsson.</p>
<p class="boundary-stacking-line-sm">Plan: Team · 4 seats</p>
<p class="boundary-stacking-line-muted">The host overlay paints over the panel because a local stacking context confines it.</p>
</div>
</neo-popover>
</div>
</neo-boundary>
</div>
</div>
</div>package examples
import "github.com/romshark/morpheus/neo"
templ BoundaryStacking() {
<div class="boundary-stacking">
<div class="boundary-stacking-overlay">
Host app overlay · z-index 100
</div>
<div class="boundary-stacking-row">
<div class="boundary-stacking-col">
@neo.Boundary(neo.BoundaryOpts{}) {
<div class="boundary-stacking-panel">
<p class="boundary-stacking-label">Default</p>
@neo.Popover(neo.PopoverOpts{Placement: neo.Set(neo.PlacementBottomStart)}) {
<neo-button { neo.PopoverTrigger()... }>Account</neo-button>
<div { neo.PopoverContent()... }>
<h3 class="boundary-stacking-heading">Account</h3>
<p class="boundary-stacking-line">Signed in as Alice Larsson.</p>
<p class="boundary-stacking-line-sm">Plan: Team · 4 seats</p>
<p class="boundary-stacking-line-muted">No stacking context. The panel's z-index 1000 paints over the host overlay.</p>
</div>
}
</div>
}
</div>
<div class="boundary-stacking-col">
@neo.Boundary(neo.BoundaryOpts{Scopes: neo.Set(neo.BoundaryScopes{Stacking: true})}) {
<div class="boundary-stacking-panel">
<p class="boundary-stacking-label">stacking</p>
@neo.Popover(neo.PopoverOpts{Placement: neo.Set(neo.PlacementBottomStart)}) {
<neo-button { neo.PopoverTrigger()... }>Account</neo-button>
<div { neo.PopoverContent()... }>
<h3 class="boundary-stacking-heading">Account</h3>
<p class="boundary-stacking-line">Signed in as Alice Larsson.</p>
<p class="boundary-stacking-line-sm">Plan: Team · 4 seats</p>
<p class="boundary-stacking-line-muted">The host overlay paints over the panel because a local stacking context confines it.</p>
</div>
}
</div>
}
</div>
</div>
</div>
}
.boundary-stacking {
position: relative;
padding: 1.5rem;
min-height: 18rem;
}
.boundary-stacking-overlay {
position: absolute;
left: 1.5rem;
right: 1.5rem;
top: 8rem;
height: 2.75rem;
z-index: 100;
display: flex;
align-items: center;
justify-content: center;
padding: 0 1rem;
border-radius: var(--page-radius);
background: #4f46e5;
box-shadow: 0 6px 16px -6px rgba(0, 0, 0, 0.45);
font-size: 0.85rem;
font-weight: 600;
color: #ffffff;
}
.boundary-stacking-row {
display: flex;
gap: 1rem;
align-items: flex-start;
}
.boundary-stacking-col {
flex: 1;
}
.boundary-stacking-panel {
min-height: 14rem;
padding: 1rem;
border: 1px dashed var(--muted);
border-radius: var(--page-radius);
}
.boundary-stacking-label {
margin: 0 0 0.75rem;
font-weight: 600;
}
.boundary-stacking-heading {
margin: 0 0 0.5rem;
}
.boundary-stacking-line {
margin: 0;
}
.boundary-stacking-line-sm {
margin: 0.5rem 0 0;
}
.boundary-stacking-line-muted {
margin: 0.5rem 0 0;
color: var(--muted);
}
Nested boundaries#
The closest boundary defines the rules; they are not inherited. The inner boundary uses scope="positioning" (no dismiss). Its popover closes on any outside click (document scope), even though the outer boundary enables dismiss scoping. A closer boundary that turns a scope off falls back to the document or viewport default, never to the outer boundary. Open the popover, then click outside it to see it close.
Outer boundary: default scope.
The closest boundary defines the rules. Dismiss is off here. A click anywhere outside this popover closes it, even though the outer boundary enables dismiss scoping. Rules are not inherited.
Inner boundary: scope="positioning" (no dismiss).
<neo-boundary>
<div class="boundary-nested-outer">
<p class="boundary-nested-intro">Outer boundary: default scope.</p>
<neo-boundary scope="positioning">
<div class="boundary-nested-inner">
<neo-popover
placement="bottom-start"
follow-scroll="until-trigger-invisible"
>
<neo-button data-neo-popover-trigger>Menu</neo-button>
<div data-neo-popover-content>
<p class="boundary-nested-body">The closest boundary defines the rules. Dismiss is off here. A click anywhere outside this popover closes it, even though the outer boundary enables dismiss scoping. Rules are not inherited.</p>
</div>
</neo-popover>
<p class="boundary-nested-caption">Inner boundary: <code>scope="positioning"</code> (no dismiss).</p>
</div>
</neo-boundary>
</div>
</neo-boundary>package examples
import "github.com/romshark/morpheus/neo"
templ BoundaryNested() {
@neo.Boundary(neo.BoundaryOpts{}) {
<div class="boundary-nested-outer">
<p class="boundary-nested-intro">Outer boundary: default scope.</p>
@neo.Boundary(neo.BoundaryOpts{Scopes: neo.Set(neo.BoundaryScopes{NoDismiss: true})}) {
<div class="boundary-nested-inner">
@neo.Popover(neo.PopoverOpts{
Placement: neo.Set(neo.PlacementBottomStart),
FollowScroll: neo.Set(neo.PopoverFollowScrollUntilTriggerInvisible),
}) {
<neo-button { neo.PopoverTrigger()... }>Menu</neo-button>
<div { neo.PopoverContent()... }>
<p class="boundary-nested-body">
The closest boundary defines the rules. Dismiss is off here. A click
anywhere outside this popover closes it, even though the outer boundary
enables dismiss scoping. Rules are not inherited.
</p>
</div>
}
<p class="boundary-nested-caption">Inner boundary: <code>{ `scope="positioning"` }</code> (no dismiss).</p>
</div>
}
</div>
}
}
.boundary-nested-outer {
padding: 2rem;
border: 1px dashed var(--muted);
border-radius: var(--page-radius);
}
.boundary-nested-intro {
margin: 0 0 1rem;
color: var(--muted);
}
.boundary-nested-inner {
min-height: 12rem;
padding: 1rem;
border: 1px dashed var(--muted);
border-radius: var(--page-radius);
}
.boundary-nested-body {
margin: 0;
}
.boundary-nested-caption {
margin-top: 2rem;
color: var(--muted);
}