Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

next: [data-state] Attribute Conflicts in Component Compositions #923

Open
max-got opened this issue Nov 14, 2024 · 2 comments
Open

next: [data-state] Attribute Conflicts in Component Compositions #923

max-got opened this issue Nov 14, 2024 · 2 comments
Labels
enhancement An improvement to an existing feature/component

Comments

@max-got
Copy link

max-got commented Nov 14, 2024

Describe the bug

Based on #823 , there's a systemic issue with component composition where multiple components using the same [data-state] attribute conflict with each other. IMO it is something to decide before 1.0. While the Toggle and Tooltip example demonstrates this problem, it affects any combination of components that rely on the [data-state](or similiar) attribute for styling and state management.

Current Behavior

  • Multiple components use the same [data-state] attribute
  • The last component in the composition overwrites the state of previous components
  • CSS selectors targeting states of overwritten components become ineffective
  • Only the last component's styles are applied

Expected Behavior

Elements should be styleable based on each component's state independently, regardless of how many components are composed together

Impact

  • Breaks component composability
  • Limits component reusability
  • Prevents proper styling of multi-state elements
  • Creates unpredictable behavior in complex component hierarchies
  • Makes debugging difficult as state conflicts are not immediately apparent

Example

Details
<Tooltip.Trigger>
	{#snippet child({ props })}
		<Toggle.Root  {...props}>
			{#snippet child({ props })}
				<button {...props}>I have [data-state] from the Toggle, not from the Tooltip.</button>
			{/snippet}
		</Toggle.Root>
	{/snippet}
</Tooltip.Trigger>

Current Style Conflicts

Details
	button[data-state='off'] {
		background-color: orange;
	}

	button[data-state='on'] {
		background-color: green;
	}

	/* These don't work due to the state conflict */
	button[data-state='closed'] {
		border: 2px solid red;
	}

	button[data-state='delayed-open'] {
		border: 2px solid blue;
	}

Proposed Solutions

Solution 1: Component-Specific Data Attributes Use distinct data attributes for each component type:

Each component gets its own attribute

/* Each component gets its own attribute */
[data-accordion-state='expanded'] { }
[data-dialog-state='open'] { }
[data-button-state='active'] { }
[data-tooltip-state='open'] { }
[data-toggle-state='on'] { }

Pros:

  • Clear separation of concerns
  • Eliminates state conflicts
  • Predictable styling behavior
  • Easier debugging
  • Scales well with multiple components

Cons:

  • Breaking change for existing implementations (even tho pre 1.0)
  • More verbose HTML output
Solution 2: Combined State Values

Use a single data-state attribute with combined values:

button[data-state~="tooltip-closed"] { }
button[data-state~="toggle-on"] { }

Pros:

  • Maintains single attribute pattern
  • Supports unlimited component combinations
  • Less breaking than separate attributes
  • More explicit state management

Cons:

  • More complex CSS selectors
  • Longer attribute values
  • More string manipulation required

Reproduction

https://stackblitz.com/edit/stackblitz-starters-xdgpeu?file=src%2Froutes%2F%2Bpage.svelte

Logs

No response

System Info

-

Severity

blocking an upgrade

@max-got max-got changed the title Data State Attribute Conflicts in Component Compositions next: [data-state] Attribute Conflicts in Component Compositions Nov 14, 2024
@huntabyte huntabyte added the enhancement An improvement to an existing feature/component label Nov 14, 2024
@ElijahJohnson5
Copy link

I am running into this same issue when using a tooltip with the collapsible component.

@huntabyte
Copy link
Owner

I'm still deciding how to approach this. I feel the explicit data-accordion-state or data-tooltip-state adds a lot of bloat and diminishes the DX for the more common use cases.

With that in mind, I also want to support more advanced use cases and composition, so perhaps we can add either a BitsConfigProvider component that would wrap the app and enable you to turn on explicit data- attributes for every component, or we expose a prop on the components that would enable explicit data attributes. I'm still a bit torn on which approach to take.

In the meantime, this can be accomplished in userland, albeit not pretty, but possible:

<Tooltip.Trigger>
	{#snippet child({ props })}
		<Toggle.Root  {...props} data-tooltip-state={props['data-state']}>
			{#snippet child({ props })}
				<button {...props} data-toggle-state={props['data-state']}>I have [data-state] from the Toggle, not from the Tooltip.</button>
			{/snippet}
		</Toggle.Root>
	{/snippet}
</Tooltip.Trigger>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement An improvement to an existing feature/component
Projects
None yet
Development

No branches or pull requests

3 participants