Skip to content

Support multi-block conditions (OR-style conditions with different at-rules) #3496

@murticalen

Description

@murticalen

Description

Allow a single condition to generate multiple independent CSS blocks, each wrapped in its own at-rule. This would enable defining hover-for-desktop + active-for-touch in one condition.

Problem Statement/Justification

When building responsive interactions, you often want hover feedback on devices with a pointer and active/press feedback on touch devices. This requires two separate @media blocks:

@media (hover: hover) { .el:hover { background: red; } }
@media (hover: none) { .el:active { background: red; } }

Currently, conditions only support [atRule, selector] pairs which nest into a single block. There's no way to define a condition that produces two independent blocks. We're forced to either:

  • Define two separate conditions (_hoverHover + _touchActive) and duplicate styles
  • Create per-property utilities with transform() that hardcode the media queries

Both workarounds scale poorly and lose the ergonomics of the condition system.

Proposed Solution or API

A new array-of-arrays syntax for conditions that generates multiple independent CSS blocks:

conditions: {
  extend: {
    hoverActive: [
      ['@media (hover: hover)', '&:is(:hover, [data-hover])'],
      ['@media (hover: none)', '&:is(:active, [data-active])'],
    ],
  },
}

Usage stays the same:

css({ _hoverActive: { bg: 'red' } })

Generated CSS:

@media (hover: hover) { .selector:is(:hover, [data-hover]) { background: red; } }
@media (hover: none) { .selector:is(:active, [data-active]) { background: red; } }

This is backward compatible — parseCondition already handles nested arrays as type: "mixed". The change would be to detect Array<[atRule, selector]> and emit separate rule blocks instead of nesting them.

Alternatives

  • Per-property custom utilities with transform() — works but requires a separate utility for each CSS property (bg, color, opacity, etc.)
  • Helper function that spreads styles into both conditions — works but TypeScript struggles with complex Panda style types
  • Using a single combined selector &:is(:hover, :active) — loses the media query isolation, causing ghost hover on touch devices

Additional Information

No response

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions