Skip to content

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 22, 2026 7:57pm

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.

usePortal?: boolean;
scrollContainer?: string;
flipOnCollision?: boolean;
hideWhenAnchorHidden?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

We might not need the last two props. Menu probably always wants to flip and hide when anchor is hidden?

</ul>
</Portal>
{:else}
<ul
Copy link
Contributor

Choose a reason for hiding this comment

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

Could make a snippet of this ul, so it's not repeated. I see the style attribute is different, but that could be a param to the snippet.

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...

@andrewzamojc
Copy link
Contributor

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

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