-
Notifications
You must be signed in to change notification settings - Fork 310
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
Styling based on parent state #373
Comments
Something like this is planned for the future. As of today, it is possible to use variables to workaround this issue without resorting to JS event handlers. (See Card.tsx under the NextJS example.) However, the CSS hover state is generally error-prone on touch-screens and seems to get stuck. It might be better to use a custom hook like useHover from |
I still don't see how to to do something like the following without JS event handlers. #one {
background-color: red;
}
#two {
background-color: green;
opacity: 0;
}
#one:hover ~ #two {
opacity: 1;
}
div {
width: 50px;
height: 50px;
}
<div id="one"></div>
<div id="two"></div> |
@martingrzzler Is this what you are after CodeSandBox demo? With this, you can show a child element when you hover over a parent. const styles = stylex.create({
tooltip: {
":hover > .tooltip": {
transform: "translateY(-100%)",
opacity: "1",
},
},
popup:{
opacity: "0",
transform: "translateY(0)",
}
}); And in your HTML you add the tooltip class. <div {...stylex.props(styles.tooltip)}>
<p>This is a tooltip</p>
<div className={`tooltip ${stylex.props(styles.popup).className}`}>StyleX is great!</div>
</div> |
@bonttimo Yes exactly! Is that somewhere to be found in the Docs? While it does feel hacky, it works. |
@martingrzzler No, it's not in the Docs. I encountered the same issue and, while experimenting, I noticed that this approach worked. It feels a bit hacky, but then it's proper CSS syntax. Hopefully, it will keep on working in the future. 😄 I observed that it works when you do: <div className={`tooltip ${stylex.props(styles.popup).className}`}>StyleX is great!</div> But then this does not work: <div className={`tooltip ${stylex(styles.popup)}`}>StyleX is great!</div> |
@bonttimo Nice! One problem I see with this is that we now have a class name .className > *
.className ~ *
.className:hover > div:first-child All of these patterns, while powerful, make styles fragile and less predictable. Applying class names on one element can affect a completely different element. |
Well, none of those selectors are supposed to be supported, especially allowing arbitrary class names in the key
This is also very hacky and shouldn't be done. |
@martingrzzler That's true. However, with this approach, you wouldn't be able to style a simple navigation with a dropdown without using JavaScript, which seems odd to me. Also, the documentation mentions "styles at a distance" but in my opinion, this approach leads to "styles from an even larger distance." because people will create one-off CSS files for basic hover effect styling. |
We've styled several huge apps at Meta with this sytem and that hasn't been the case. |
@bonttimo Using the This hack is not a supported pattern and will break in the future. Sibling selectors are currently unsupported in StyleX. |
@necolas So how did you deal with case like this in Meta using stylex system only? .parent {
background-color: red;
}
.parent .child {
background-color: blue;
}
.parent:hover .child {
background-color: green;
} @nmn do you have any alternative to that hack or to the mouse event detect hover approach? |
@Frankyfrankly As mentioned, the CSS |
@nmn Does that mean that we cannot use stylex ever to override styles from an existing UI component library? Most libraries, especially older ones, only expose a single className and expect you to overwrite styles using styling at a distance. |
Yes that's pretty much correct. StyleX is a system for encapsulating styles; libraries that allow arbitary styling of their sub-tree is the exact pattern StyleX is designed to avoid. |
@philipfeldmann StyleX is not a replacement for CSS, it's a component styling system. If you need selectors, do not use StyleX. |
Just wanted to jump into the conversation. I've tried a couple of approaches. I initially tried just using event handlers but it felt pretty unwieldy to me and there were edge cases where if the cursor jumped outside of the browser the One of the reasons I struggle with event handlers for this is because let's say I have a hook called Now I'm using CSS variables to compose group variants, and while it requires a little coordination it feels fairly reasonable: const cssVarTextAltOpacity = "--text-alt-opacity"
const dropdownStyles = create({
// ...
listItem: {
[cssVarTextAltOpacity]: {
default: "0.375",
":is(:hover, :focus-visible)": "1",
},
alignItems: "center",
backgroundColor: {
":is(:hover, :focus-visible)": "#0c8ce9", // TODO: Extract to CSS variable
},
display: "flex",
gap: 8,
paddingBlock: 2, // 18 + 2 * 2 = 24
paddingInline: 16,
},
// ...
listItemTextAlt: {
opacity: `var(${cssVarTextAltOpacity})`,
},
}) The nuance with not using event handlers is that the code isn't portable to RN code bases (not a pressing concern for me but a valid one). I'd love to see how people are implementing the Edit: Here's an example of my use case for why I want to use group variant. Notice the text goes full white when the list item is hovered. asdasdasdasdasd.mp4 |
Quick follow up: I just found that even CSS doesn't know when the cursor leaves the browser window (by pressing command-tab the browser loses focus and the cursor can move freely without triggering the :hover to release). Here's the simple CodePen: https://codepen.io/zaydek/pen/dyrBPRv. This is something I run into a lot in Figma plugins because your window is a 400px x 400px rectangle and the cursor can 'jump' out of the plugin window for many reasons. I didn't realize CSS has more or less the same problems though. Built-in.Retina.Displayasdasd.mp4Is there a canonical implementation for useHover that is advised for composing group variants? I know it's probably simple but I'm curious if anyone has a solution that is battle hardened for their use case. |
There's one in the react-native-web source code. It's a little outdated as today we'd only need to use PointerEvent, but it should get you started |
@zaydek There's an example of using CSS variables to do styling based on parent state here.
|
Thanks guys. This clears up some confusion I had. I'll surface both links here in case anyone is interested: Spectrum: https://github.com/adobe/react-spectrum/blob/main/packages/%40react-aria/interactions/src/useHover.ts @nmn Besides the constraint of requiring a separate file this looks pretty good:
It's a small thing but it's nice you don't have to interpolate the variable in |
IIRC the Adobe hook is based on an even older, experimental event hook I wrote while on the React core team. I wrote another hover hook for the project that RSD was extracted from. That one supports control over bubbling too. We might open source those hooks in a separate package once React Native supports EventTarget on host elements |
If you do, the use cases I would want to compose are @nmn I wonder if it would make sense to expose a utility For example: const transform = cssVar()
const styles = stylex.create({
foo: {
[transform]: {
default: "translateY(0px)",
":is(:hover, :focus-visible)": "translateY(2px)",
},
}
bar: {
transform,
},
}) It's a small quality of life thing but if the API did exist, it may help address users who want to style using 'side effects' in a safer way with less boilerplate. But it could also lead to confusion if users try to start manipulating |
Yeah we wrote hooks for all of those, which is where the Adobe team got the idea from. I agree that for the most part having the CSS capability is best for simple use cases, although hover is fundamentally not great for touch experiences on web. However, having hooks for more complex use cases related to supporting reactive changes to the DOM tree is also handy. Hopefully there is a path for React Native to support the CSS syntax natively, but in the mean time we can probably polyfill more of the CSS syntax for RN in the same way we've polyfilled |
We will be looking into allowing
Please don't combine
Here's adobe article explaining the issues with hover on touch screens and why there is no perfect solution without JS: https://react-spectrum.adobe.com/blog/building-a-button-part-2.html |
@nmn I got funny results with this testing on different phones: mui/mui-x#10039 (comment). I'm curious it seems that |
Sadly it's not a good enough solution when you take into account devices where both a touchscreen and a mouse pointer exist. A large majority of new Windows laptops have touchscreens and iPads exist, so this is a relatively common occurrence. |
Describe the feature request
I have a simple
Tooltip
component that uses the Tailwindgroup
class to change the styles of a child div when thegroup
is hovered. With StyleX embracing encapsulation there seems to be no easy way of doing this other than usinguseState
withmouseenter
andmouseleave
events. Is that how one is supposed to handle these scenarios?The text was updated successfully, but these errors were encountered: