facebook / stylex

StyleX is the styling system for ambitious user interfaces.
https://stylexjs.com
MIT License
8.22k stars 303 forks source link

[RFC] Ref - A solution for complex CSS selectors #535

Closed aspizu closed 2 months ago

aspizu commented 2 months ago

Describe the feature request

Motivation

Currently, stylex does not support complex CSS selectors like :hover, :focus, :active based on a specific element such as a parent or a sibling.

A ref is a object which allows you to reference a specific element in the DOM.

API Style: A

Allow to reference a ref defined in local scope.

const ref = stylex.ref()

const styles = stylex.create({
    parent: {
        background: 'blue'
    },
    child: {
        [ref(':hover')]: {
            background: 'red'
        }
    }
})

<div {...stylex.ref(ref).props(styles.parent)}>
    <div {...stylex.props(styles.child)}>
        ...
    </div>
</div>

API Style: B

Do not allow to reference a ref without passing it as a parameter.

const ref = stylex.ref()

const styles = stylex.create({
    parent: {
        background: 'blue'
    },
    child: (ref: stylex.Ref) => {
        [ref(':hover')]: {
            background: 'red'
        }
    }
})

<div {...stylex.ref(ref).props(styles.parent)}>
    <div {...stylex.props(styles.child(ref))}>
        ...
    </div>
</div>

Implementation

stylex.ref(ref).props(...) will add a unique class name (ex: .ref_1) to the element.

The call syntax ref(':hover') will be compiled down into a selector string with the class name prefixed (ex: ._ref_1:hover .atomicClass).

Considerations

With Style A, it is easier to use ref as it is defined in the local scope. However, it may be harder to understand where the ref is coming from if the styles are defined in a different file. Also, it is not possible to override the ref.

With Style B, each usage of a style which uses refs will need to pass the ref as a parameter. But it is unclear wether this style is a dynamic style or static style.

nmn commented 2 months ago
  1. There is no need for refs
  2. We don't want to add an API that depends on React APIs.

I've shared an RFC with design proposal that enables the same feature:

https://github.com/facebook/stylex/issues/536

Please comment on that issue with your thoughts. Thank you!

aspizu commented 2 months ago

How does this depend on React API?

nmn commented 2 months ago

@aspizu My bad. I didn't read carefully enough and the ref threw me off. Your proposal is actually very similar to what I have written up in the RFC.

There is one problem with the API you proposed which is that stylex.ref() works in two different ways. This won't be type-safe and could cause issues for the compiler.

The other issue I see is that ref(':hover') does not say where to look for the reference. This would mean that every single complex selector would depend on a unique ID in CSS. This design would be untenable as it would lead to bloat in the CSS. We need a design that encourages maximum re-use of the same IDs within different contexts.

Other than this, the approach you suggest makes sense is also what I have suggested in the linked RFC.