Problem
Herb's development-mode debug overlay wraps each <%= tag.* %> ERB output in . While display: contents correctly makes these wrappers invisible to CSS layout, it does not affect the DOM tree structure. This breaks CSS sibling combinators (~, +), which operate on DOM siblings, not layout siblings.
This is particularly impactful for Tailwind CSS peer-* variants (e.g., peer-checked:, peer-focus:), which rely on the general sibling combinator (~) to style elements based on a sibling's state.
Reproduction
Minimal ERB using Rails tag.* helpers and Tailwind's Flowbite toggle pattern:
<%= tag.label(class: 'relative inline-flex cursor-pointer items-center') do %>
<%= tag.input(type: 'checkbox', class: 'sr-only peer', checked: true) %>
<%= tag.div(class: 'peer h-6 w-11 rounded-full bg-gray-200 peer-checked:bg-blue-600') %>
<% end %>
Expected DOM (without Herb)
<label class="relative inline-flex cursor-pointer items-center">
<input type="checkbox" class="sr-only peer" checked>
<div class="peer h-6 w-11 rounded-full bg-gray-200 peer-checked:bg-blue-600"></div>
</label>
input.peer:checked ~ div.peer-checked:bg-blue-600 matches → toggle shows blue.
Actual DOM (with Herb debug overlay)
<label class="relative inline-flex cursor-pointer items-center">
<span data-herb-debug-outline-type="erb-output component" style="display: contents">
<input type="checkbox" class="sr-only peer" checked>
</span>
<span data-herb-debug-outline-type="erb-output component" style="display: contents">
<div class="peer h-6 w-11 rounded-full bg-gray-200 peer-checked:bg-blue-600"></div>
</span>
</label>
input and div are no longer DOM siblings → CSS ~ combinator doesn't match → toggle always
appears gray/off, even when checked.
Impact
- Visual state is lost: Checked checkboxes appear unchecked because peer-checked: never activates
- Affects all peer-* variants: peer-focus:, peer-hover:, peer-disabled:, etc.
- Also affects group-* variants if tag.* helpers are used (same ~ combinator pattern)
- Adjacent sibling combinator (+) is equally affected
- Flowbite toggle components are a common pattern that breaks completely
Workaround
Disabling Herb's debug overlay (via validation_mode: :none) resolves the issue. Production is unaffected since the overlay is development-only.
Environment
- herb: 0.8.9
- Ruby: 3.4.8
- Rails: 8.1
- Tailwind CSS: v4
Problem
Herb's development-mode debug overlay wraps each <%= tag.* %> ERB output in . While display: contents correctly makes these wrappers invisible to CSS layout, it does not affect the DOM tree structure. This breaks CSS sibling combinators (~, +), which operate on DOM siblings, not layout siblings.
This is particularly impactful for Tailwind CSS peer-* variants (e.g., peer-checked:, peer-focus:), which rely on the general sibling combinator (~) to style elements based on a sibling's state.
Reproduction
Minimal ERB using Rails tag.* helpers and Tailwind's Flowbite toggle pattern:
Expected DOM (without Herb)
input.peer:checked ~ div.peer-checked:bg-blue-600 matches → toggle shows blue.
Actual DOM (with Herb debug overlay)
input and div are no longer DOM siblings → CSS ~ combinator doesn't match → toggle always
appears gray/off, even when checked.
Impact
Workaround
Disabling Herb's debug overlay (via validation_mode: :none) resolves the issue. Production is unaffected since the overlay is development-only.
Environment