Skip to content

Conversation

@braden-w
Copy link
Contributor

@braden-w braden-w commented Jul 5, 2025

Problem Description

The Select component had a UX issue where dropdown content with long text would expand beyond the trigger's width, creating an inconsistent and unprofessional appearance. This created a poor user experience, especially in scenarios where:

  • Select options contained descriptive text longer than the trigger width
  • The trigger was intentionally kept narrow for UI consistency (e.g., w-[120px])
  • The dropdown would expand to accommodate the content, breaking the visual alignment with the trigger

The issue manifested as the dropdown being much wider than the trigger button, creating a jarring visual disconnect.

Solution Discovery Process

During the investigation, I experimented with several CSS approaches to solve this width consistency issue:

  1. w-max - Initially tried to let the content width grow to fit its content, but this didn't play well with the existing min-width constraint from min-w-(--bits-select-anchor-width)

  2. max-w-none - Attempted to remove any maximum width constraints, but this could lead to unbounded growth and didn't provide the precise control needed

  3. w-auto - Considered letting the browser automatically determine width, but this didn't resolve the fundamental constraint issue

  4. max-w-min - The winning solution! This sets max-width: min-content, which constrains the dropdown width while allowing content to wrap naturally when it exceeds the trigger width

Code Changes

The fix required a minimal, single-line addition to the Select.Content component:

// select-content.svelte
class={cn(
-  "bg-popover text-popover-foreground ... min-w-[8rem] overflow-y-auto overflow-x-hidden ...",
+  "bg-popover text-popover-foreground ... min-w-[8rem] max-w-min overflow-y-auto overflow-x-hidden ...",
  className
)}

Minimal Reproduction Example

<script lang="ts">
  import * as Select from "$lib/components/ui/select";
  let value = $state("");
</script>

<Select.Root type="single" bind:value>
  <Select.Trigger>
    {value || "This trigger is narrower than the select content"}
  </Select.Trigger>
  <Select.Content class="max-w-min">
    <Select.Item value="short">Short option</Select.Item>
    <Select.Item value="long">This is a very long option that should overflow the narrow trigger</Select.Item>
    <Select.Item value="description">Option with description - Lorem ipsum dolor sit amet</Select.Item>
  </Select.Content>
</Select.Root>

Benefits

  1. Consistent Visual Design: The dropdown now maintains a consistent width relationship with the trigger
  2. Better Content Wrapping: Long text wraps naturally instead of expanding the dropdown horizontally
  3. Zero Breaking Changes: This is purely an enhancement - existing implementations continue to work exactly as before
  4. Minimal Code Impact: A single CSS class addition solves the problem elegantly

Demo

Before the fix:

┌─────────────┐
│ Pick     ▼  │ (Trigger: 120px wide)
└─────────────┘
       ↓
┌──────────────────────────────────────┐
│ This is a very long option that      │ (Dropdown expands beyond trigger width)
│ should overflow the narrow trigger   │
└──────────────────────────────────────┘

CleanShot 2025-07-04 at 19 41 44

After the fix:

┌─────────────┐
│ Pick     ▼  │ (Trigger: 120px wide)
└─────────────┘
       ↓
┌─────────────┐
│ This is a   │ (Dropdown respects trigger width,
│ very long   │  content wraps to multiple lines)
│ option that │
│ should      │
│ overflow    │
│ the narrow  │
│ trigger     │
└─────────────┘

CleanShot 2025-07-04 at 19 42 30

This fix ensures that shadcn-svelte's Select component maintains visual consistency between the trigger and dropdown, matching the quality and attention to detail users expect from the library.

- Add max-w-min to Select.Content to prevent dropdown from expanding beyond trigger width
- Fixes overflow issues with Select.Content
@changeset-bot
Copy link

changeset-bot bot commented Jul 5, 2025

⚠️ No Changeset found

Latest commit: 87dfa58

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions
Copy link
Contributor

github-actions bot commented Jul 5, 2025

built with Refined Cloudflare Pages Action

⚡ Cloudflare Pages Deployment

Name Status Preview Last Commit
shadcn-svelte ✅ Ready (View Log) Visit Preview 87dfa58

@braden-w
Copy link
Contributor Author

braden-w commented Jul 5, 2025

On second thought, I'm wondering if this should actually be the default behavior. While the visual consistency is nice, there are valid use cases where allowing the dropdown to expand beyond the trigger width improves usability:

  1. Readability: When options have long descriptive text, horizontal expansion can be more readable than wrapping to multiple lines
  2. Scanning: Users can more quickly scan options when they're displayed on a single line
  3. Accessibility: Some users might find horizontally expanded content easier to read than wrapped text

Perhaps instead of making this the default, we could make it an optional behavior? Something like:

<\!-- Default behavior: dropdown can expand -->
<Select.Content>
  ...
</Select.Content>

<\!-- Opt-in to constrained width -->
<Select.Content class="max-w-min">
  ...
</Select.Content>

Or even a prop:

<Select.Content constrainWidth={true}>
  ...
</Select.Content>

This way users can choose the behavior that best fits their use case. What do you think?

@huntabyte
Copy link
Owner

I think this should definitely be opt-in. Rather than the prop I think that class makes the most sense here otherwise someone could set constrainWidth={true} and then also set a custom max-w- which would basically cancel out what we've applied if that makes sense.

@braden-w
Copy link
Contributor Author

braden-w commented Jul 8, 2025

Yes, I totally agree. Would this be too much to include in the docs? This does seem be a strange fix (I never really use max-w-min), but it might be useful enough and weird enough of a solution that it might be worth including for others in the future.

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.

2 participants