stylex: stylex.include not working in 0.5.x

Describe the issue

In a previous version of Stylex (^0.4.1), I was able to do something like the following:

Screenshot 2024-01-27 at 6 20 28 PM

The pattern I was specifically interested in is the following:

	primary: {
		...stylex.include(typography.paragraph),
		alignItems: "center",
		backgroundColor: "rgb(255, 255, 255, 0.0625)",
		borderRadius: 1e3,
		boxShadow: "0 0 0 1px rgb(255, 255, 255, 0.0625)",
		color: "rgb(255, 255, 255, 0.25)",
		display: "flex",
		fontWeight: 600,
		justifyContent: "center",
		paddingBlock: 12,
		paddingInline: 16,
	},

So I could inline the typography and simply use stylex.stylex(styles.button) instead of stylex.props(styles.button, typography.paragraph, ...). In order for this to work, I needed to use the since deprecated API stylex.stylex so it appears this behavior is no longer suported.

By looking at https://github.com/facebook/stylex/issues/136 I can see that the API for include itself is intentionally undocumented so as to see if the community really needs this feature.

In ^5.0.8 it looks like the feature is not supported at all. Here’s a repro: https://github.com/zaydek/experimental-vite-stylex-eslint. (I am simply trying to compose a center style, more or less the same use case.)

My use case is for things like compound utiltiies (e.g. typography, centering, text truncation, etc.) I want to be able to inline these styles in the stylesheet API so it’s colocated. The other benefit of this approach is that when I implement the styles, I can easily add conditional variants such as stylex.props(styles.input, isError && ...) instead of something like this stylex.props(styles.typography, typography.isCenter, styles.input, styles.isError && ...). The other small benefit is that it’s slightly easier to annotate using comments using the stylesheet API, because it’s tall text rather than wide text.

Expected behavior

I’d like to be able to use stylex.include if it’s still supported because colocation is good and it makes for less verbose code and it’s easier to annotate with comments.

Steps to reproduce

Repro is here https://github.com/zaydek/experimental-vite-stylex-eslint. This demonstrates that the feature doesn’t work.

Test case

No response

Additional comments

No response

About this issue

  • Original URL
  • State: closed
  • Created 5 months ago
  • Comments: 18 (7 by maintainers)

Most upvoted comments

It’s still not clear to me? But you can use [a, b] for style composition without ever having to use includes. That will produce smaller bundles (no duplication of declarations in the compiled output), probably have marginally faster runtime merging, and can avoid whatever bugs currently exist with the includes implementation.

styleq: __included_414__ typeof [object Object] is not 'string' or 'null'.

This error comes from here:

https://github.com/necolas/styleq/blob/main/src/styleq.js#L107-L114

It’s telling you that the value of a key in the compiled data is not a string, which suggests the source code (in this case I’m assuming it’s an include call) is either not compiled or is being compiled to an incorrect format.

I don’t think you need to use include (especially if it’s causing problems), because you can just pass the imported style to the props function instead of spreading it into a create object.

@nmn For what it’s worth it looks like I can simply use stylex.props(null, …) and triggers the deoptimization all the same.

yes, it was more of a compositional preference. will do simply with stylex.props.

Screenshot 2024-01-28 at 8 52 15 PM

Oh I forogt to mention I noticed this terminal log. I don’t know if it’s related but I believe it only started appearing once I tried to use stylex.include in this way.

@nmn Thanks! I wanted to follow up since I tried the export trick you mentioned but using:

"@stylexjs/babel-plugin": "^0.5.1",
"@stylexjs/eslint-plugin": "^0.5.1",
"vite": "^5.0.8",
"vite-plugin-stylex-dev": "^0.2.5"

I still can’t get spread to work. I did try exporting everything but to no avail. I believe you only meant that center in this case needed to be exported but I exported everything to be certain. Here’s the snippet for reference:

import * as stylex from "@stylexjs/stylex"

export const center = stylex.create({
  center: {
    alignItems: "center",
    display: "flex",
    justifyContent: "center",
  },
})

export const styles = stylex.create({
  container: {
    ...stylex.include(center.center),
    blockSize: "100vh",
  },
  button: {
    alignItems: "center",
    backgroundColor: {
      default: "blue",
      ":hover": "orange",
      ":active": "red",
    },
    display: "flex",
    gap: 8,
    padding: 16,
    borderRadius: 14,
    color: {
      default: "gray",
      ":hover": "white",
    },
  },
  icon: {
    blockSize: 16,
    inlineSize: 16,
  },
})

export function CentralCamera4FilledOffStroke2Radius2(props: JSX.IntrinsicElements["svg"]) {
  return (
    <svg {...props} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
      <path d="M21 17V7C21 5.89543 20.1046 5 19 5H10.0858C9.71071 5 9.351 4.851 9.08579 4.58579C8.71071 4.21071 8.20201 4 7.67157 4H6.32843C5.79799 4 5.28929 4.21071 4.91421 4.58579L4.5 5.06301C3.63739 5.28503 3 6.06808 3 7V17C3 18.1046 3.89543 19 5 19H19C20.1046 19 21 18.1046 21 17Z" stroke="currentColor" strokeWidth="2" strokeLinejoin="round" />
      <path d="M17 12C17 13.6569 15.6569 15 14 15C12.3431 15 11 13.6569 11 12C11 10.3431 12.3431 9 14 9C15.6569 9 17 10.3431 17 12Z" stroke="currentColor" strokeWidth="2" strokeLinejoin="round" />
      <path d="M7 9H7.01" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
    </svg>
  )
}

export function App() {
  return (
    <div {...stylex.props(styles.container)}>
      <div {...stylex.props(styles.button)}>
        <CentralCamera4FilledOffStroke2Radius2 {...stylex.props(styles.icon)} />
        <div>Hello, World!</div>
      </div>
    </div>
  )
}

Just let me know if I’m doing something wrong. Here’s the repro for reference: https://github.com/zaydek/experimental-vite-stylex-eslint/blob/main/src/App.tsx.

You question about using stylex.include is correct. As stylex.include is under evaluation, I will take this as a vote for making it an official API. If the export trick works, you can continue using stylex.include. I will try and fix the bugs soon.

My inclination is that stylex.include can help a lot with nuances like void elements such as <input> where you can’t simply use a typographic component like <Heading>, <Paragraph> and you have to merge styles. This comes up in other places but for the time being I like the idea that there’s a back door for these edge cases where it’d be really nice to colocate logic in one place. With more experience I may change my mind but at present I’d just like to experiment more.

Looking forward to a fix if that’s necessary. Happy to report if any future fixes work.