Skip to content

Add Portal component#3107

Merged
laurakwhit merged 10 commits intomainfrom
portal-component
Jan 28, 2026
Merged

Add Portal component#3107
laurakwhit merged 10 commits intomainfrom
portal-component

Conversation

@laurakwhit
Copy link
Collaborator

@laurakwhit laurakwhit commented Jan 22, 2026

Description & motivation 💭

This PR introduces a new Portal component to the Holocene design system and integrates it with the existing Menu component via an opt-in usePortal prop.

Portal renders children at a specific position relative to an anchor element with advanced positioning features:

  • Renders content in document.body to escape overflow/z-index constraints
  • Has 8 position options (top, bottom, left, right, top-left, top-right, bottom-left, bottom-right)
  • Automatically flips position when content would overflow viewport or scroll container if flipOnCollision is enabled
  • Tracks scrolling in custom containers, not just window with the optional scrollContainer prop
  • Hides portal content when the anchor element scrolls out of view if hideWhenAnchorHidden is enabled
<script>
  import { Portal } from '$lib/holocene/portal';
  import Button from '$lib/holocene/button.svelte';

  let isOpen = $state(false);
</script>

<Button id="portal-trigger" on:click={() => (isOpen = !isOpen)}>Toggle Portal</Button>

<Portal anchor="portal-trigger" open={isOpen}>
  <div>Portal content</div>
</Portal>

Screenshots (if applicable) 📸

Design Considerations 🎨

Testing 🧪

How was this tested 👻

  • Manual testing
  • E2E tests added
  • Unit tests added
  • Storybook stories

Steps for others to test: 🚶🏽‍♂️🚶🏽‍♀️

Checklists

Draft Checklist

Merge Checklist

Issue(s) closed

Related to DT-3325

Docs

Any docs updates needed?

@vercel
Copy link

vercel bot commented Jan 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
holocene Ready Ready Preview, Comment Jan 28, 2026 0:22am

Request Review

Copy link
Contributor

@andrewzamojc andrewzamojc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just curious about the positioning props on the portal.

let {
anchor,
open = true,
position = 'bottom',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this mixing concepts between portal and overlay? I haven't gone this deep on portals but I didn't think they had a position like a popover...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is a positioned portal. We could consider having just a portal component and then handling the positioning separately, but I figured for 99% of our use cases we're going to want to position it.

In my experience an overlay wouldn't need to be positioned since it's usually the same (centered) in every instance. Is that not your experience? Curious if you have any more insights into patterns for portals you've seen and liked!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya I see. Basically this is practical and always how we're going to use it 👍

@andrewzamojc
Copy link
Contributor

This is great stuff! Pretty cool to see the guts of a Portal.

Copy link
Contributor

@andrewzamojc andrewzamojc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

$menuElementCtx = menuElement;
});

$effect(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Named function please

@laurakwhit laurakwhit merged commit f46dca0 into main Jan 28, 2026
20 checks passed
@laurakwhit laurakwhit deleted the portal-component branch January 28, 2026 18:09
temporal-cicd bot pushed a commit that referenced this pull request Feb 4, 2026
Auto-generated version bump from 2.44.1 to 2.45.0

Bump type: minor

Changes included:
- [`4d828b96`](4d828b9) Account for potential not in conditional in custom query (#3081)
- [`bead6b37`](bead6b3) Fix truncate in detail-list-text-value (#3089)
- [`bb4bee6c`](bb4bee6) Bump svelte dependencies (#3097)
- [`586bfe19`](586bfe1) Test section
- [`4ad6cfff`](4ad6cff) Remove sisyphus and add to .gitignore (#3104)
- [`0aea80f3`](0aea80f) Nav update icons clean (#3095)
- [`bca75cc5`](bca75cc) make routes conditional on prop (#3106)
- [`0008100b`](0008100) Add new Nexus timeout fields and translations (#3105)
- [`b9939675`](b993967) Bump golang.org/x/crypto from 0.38.0 to 0.45.0 in /server (#3028)
- [`5963ca9c`](5963ca9) Nav release fix/kt (#3110)
- [`3cab346e`](3cab346) quick nav fix add logo and route to title (#3112)
- [`f9f75d64`](f9f75d6) [DT-3501] reactive timestamp (#3108)
- [`faa48e2b`](faa48e2) [DT-3501][DT-3505] 12/24 hour and ISO formats added to timezone popover (#3113)
- [`068e5978`](068e597) Fix nav collapse icon (#3117)
- [`d0b34226`](d0b3422) feat(workflows): show Versioning Behavior column when filtering by deployment version (#3120)
- [`bd8a3556`](bd8a355) [DT-3580] Add tooltips to worker deployment version status badges (#3119)
- [`f46dca00`](f46dca0) Add Portal component (#3107)
- [`ef6b923e`](ef6b923) feat(deployments): make build ID and deployment name copyable and filterable (#3121)
- [`6209f9b6`](6209f9b) DT-3507-fix-code-block-scrolling (#3129)
- [`023f034b`](023f034) DT-3523 - standalone activities UI (#3124)
- [`7199ce9e`](7199ce9) Set API to v1.60.0 (#3132)
temporal-cicd bot pushed a commit that referenced this pull request Feb 4, 2026
Auto-generated version bump from 2.44.1 to 2.45.0

Bump type: minor

Changes included:
- [`4d828b96`](4d828b9) Account for potential not in conditional in custom query (#3081)
- [`bead6b37`](bead6b3) Fix truncate in detail-list-text-value (#3089)
- [`bb4bee6c`](bb4bee6) Bump svelte dependencies (#3097)
- [`586bfe19`](586bfe1) Test section
- [`4ad6cfff`](4ad6cff) Remove sisyphus and add to .gitignore (#3104)
- [`0aea80f3`](0aea80f) Nav update icons clean (#3095)
- [`bca75cc5`](bca75cc) make routes conditional on prop (#3106)
- [`0008100b`](0008100) Add new Nexus timeout fields and translations (#3105)
- [`b9939675`](b993967) Bump golang.org/x/crypto from 0.38.0 to 0.45.0 in /server (#3028)
- [`5963ca9c`](5963ca9) Nav release fix/kt (#3110)
- [`3cab346e`](3cab346) quick nav fix add logo and route to title (#3112)
- [`f9f75d64`](f9f75d6) [DT-3501] reactive timestamp (#3108)
- [`faa48e2b`](faa48e2) [DT-3501][DT-3505] 12/24 hour and ISO formats added to timezone popover (#3113)
- [`068e5978`](068e597) Fix nav collapse icon (#3117)
- [`d0b34226`](d0b3422) feat(workflows): show Versioning Behavior column when filtering by deployment version (#3120)
- [`bd8a3556`](bd8a355) [DT-3580] Add tooltips to worker deployment version status badges (#3119)
- [`f46dca00`](f46dca0) Add Portal component (#3107)
- [`ef6b923e`](ef6b923) feat(deployments): make build ID and deployment name copyable and filterable (#3121)
- [`6209f9b6`](6209f9b) DT-3507-fix-code-block-scrolling (#3129)
- [`023f034b`](023f034) DT-3523 - standalone activities UI (#3124)
- [`7199ce9e`](7199ce9) Set API to v1.60.0 (#3132)
- [`138473c6`](138473c) Set API to v1.60.1 (#3134)
laurakwhit added a commit that referenced this pull request Feb 4, 2026
Auto-generated version bump from 2.44.1 to 2.45.0

Bump type: minor

Changes included:
- [`4d828b96`](4d828b9) Account for potential not in conditional in custom query (#3081)
- [`bead6b37`](bead6b3) Fix truncate in detail-list-text-value (#3089)
- [`bb4bee6c`](bb4bee6) Bump svelte dependencies (#3097)
- [`586bfe19`](586bfe1) Test section
- [`4ad6cfff`](4ad6cff) Remove sisyphus and add to .gitignore (#3104)
- [`0aea80f3`](0aea80f) Nav update icons clean (#3095)
- [`bca75cc5`](bca75cc) make routes conditional on prop (#3106)
- [`0008100b`](0008100) Add new Nexus timeout fields and translations (#3105)
- [`b9939675`](b993967) Bump golang.org/x/crypto from 0.38.0 to 0.45.0 in /server (#3028)
- [`5963ca9c`](5963ca9) Nav release fix/kt (#3110)
- [`3cab346e`](3cab346) quick nav fix add logo and route to title (#3112)
- [`f9f75d64`](f9f75d6) [DT-3501] reactive timestamp (#3108)
- [`faa48e2b`](faa48e2) [DT-3501][DT-3505] 12/24 hour and ISO formats added to timezone popover (#3113)
- [`068e5978`](068e597) Fix nav collapse icon (#3117)
- [`d0b34226`](d0b3422) feat(workflows): show Versioning Behavior column when filtering by deployment version (#3120)
- [`bd8a3556`](bd8a355) [DT-3580] Add tooltips to worker deployment version status badges (#3119)
- [`f46dca00`](f46dca0) Add Portal component (#3107)
- [`ef6b923e`](ef6b923) feat(deployments): make build ID and deployment name copyable and filterable (#3121)
- [`6209f9b6`](6209f9b) DT-3507-fix-code-block-scrolling (#3129)
- [`023f034b`](023f034) DT-3523 - standalone activities UI (#3124)
- [`7199ce9e`](7199ce9) Set API to v1.60.0 (#3132)
- [`138473c6`](138473c) Set API to v1.60.1 (#3134)

Co-authored-by: laurakwhit <15069288+laurakwhit@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants