Describe the feature request
It would be helpful to have more options for propLimits, in order to disallow certain literal values and instead require using design tokens defined with stylex.defineVars() or stylex.defineConsts(). Here is a strawman proposal.
Example
// eslint.config.js
export default [
{
ignores: ["dist/**"],
},
{
files: ["src/**/*.{ts,tsx}"],
plugins: {
"@stylexjs": stylexPlugin,
},
rules: {
"@stylexjs/valid-styles": [
"error",
{
propLimits: {
backgroundColor: {
limit: {
variable: true,
},
reason: "must pass a StyleX variable",
},
borderColor: {
limit: {
variable: {
name: ["colors", "scales"],
},
},
reason:
"must pass one of the StyleX variables `colors` or `scales`",
},
color: {
limit: {
variable: {
from: "./src/lib/tokens.stylex.ts",
name: ["colors", "scales"],
},
},
reason:
"must pass one of the StyleX variables `colors` or `scales`",
},
marginLeft: {
// array for unions of acceptable values, similar to existing behavior
limit: [
"auto",
{
variable: true,
},
],
reason:
"must pass a StyleX variable, or the literal string 'auto'",
},
outlineColor: {
limit: [
{ null: true },
{
variable: {
from: "./src/lib/tokens.stylex.ts",
name: ["colors"],
property: ["destructive", "success"],
},
},
],
reason:
"must pass one of the StyleX variables `colors` or `scales`",
},
paddingLeft: {
limit: {
cssVar: true,
},
reason: "must pass a CSS variable",
},
paddingRight: {
limit: {
cssVar: ["--padding-sm", "--padding-lg"],
},
reason:
"must pass one of the CSS variables `--padding-sm` or `--padding-lg`",
},
zIndex: {
limit: [
{
const: {
from: "./src/lib/tokens.stylex.ts",
// name can be a string if there is only one
name: "layers",
},
},
{
calc: true,
},
],
reason: "must pass a `layers.*` constant from #/lib/tokens.stylex.ts, or a calc() expression",
},
},
},
],
},
},
];
// component.tsx
import * as stylex from "@stylexjs/stylex";
import { colors, scales as goodScales, spacing } from "#/lib/tokens.stylex.ts";
import { scales as evilScales } from "#/lib/tokens-evil.stylex.ts";
const styles = stylex.create({
invalid: {
// invalid: must use a StyleX variable
backgroundColor: "red",
// invalid: must use a StyleX variable from the `colors` or `scales` object
borderColor: spacing.medium,
// invalid: must use a StyleX variable from the `colors` or `scales` object, and only from `lib/tokens/stylex.ts`
color: evilScales.blue500,
// invalid: must use a StyleX variable, or the literal string 'auto'
marginLeft: "1em",
// invalid: can only use `colors.destructive` or `colors.success` for outlineColor
outlineColor: colors.blue,
// invalid: must pass a CSS variable
paddingLeft: spacing.medium,
// invalid: must pass one of the CSS variables `--padding-sm` or `--padding-lg`
paddingRight: "var(--padding)",
// invalid: must pass a StyleX `layers.*` constant or a calc() expression
zIndex: 1,
},
valid: {
// semantically wrong but valid according to above configuration
backgroundColor: spacing.md,
borderColor: colors.blue500,
// validation is against the original export name, not the import name
color: goodScales.blue500,
// literal value "auto" explicitly allowed
marginLeft: "auto",
outlineColor: {
":focus": colors.success,
// this is why we needed {null: true} in the config
default: null,
},
paddingLeft: "var(--padding)",
paddingRight: "var(--padding-sm)",
zIndex: "calc(var(--dialog-level) * 10)",
},
});
Specification
Variables
{ variable: true } means a variable defined with stylex.defineVars() must be used.
Passing an object allows further constraining the variable:
-
variable.name allows constraining the exported name of the variable. It can be a single string, or an array of strings.
Potentially could allow globs like pink* to include pink100 through pink900, but this is problematic because a perverse person could use ["pink*"] as a JavaScript key.
-
variable.property allows constraining which property on the variable is used
-
variable.from allows constraining the file the variable is imported from. This is specified by the path relative to the ESLint config file; it is validated after resolving subpath imports and import aliases
Constants
{const: true} and {const: { name: "colors" }} behaves the same as variables, but for stylex.defineConsts() instead of stylex.defineVars().
CSS variables
{ cssVar: true } allows var() expressions. Passing an object allows constraining the name of the variable.
CSS calculations
{calc: true} allows calc() expressions. In this strawman proposal it does not accept further customization.
null
{ null: true } allows null as a value (e.g. for default case in nested blocks)
Remarks
This proposal is really several individual features; for my current use case I'd be quite happy with just being able to do [{null: true}, {variable: true}].
In the current implementation, limit: null is equivalent to limit: {variable: true} since variable values are not validated at all; however, this is causing problems with default: null in nested configs. I am getting around this by doing
export const nullHack = stylex.defineVars({
null: null,
});
which is awkward.
Describe the feature request
It would be helpful to have more options for
propLimits, in order to disallow certain literal values and instead require using design tokens defined withstylex.defineVars()orstylex.defineConsts(). Here is a strawman proposal.Example
Specification
Variables
{ variable: true }means a variable defined withstylex.defineVars()must be used.Passing an object allows further constraining the variable:
variable.nameallows constraining the exported name of the variable. It can be a single string, or an array of strings.Potentially could allow globs like
pink*to includepink100throughpink900, but this is problematic because a perverse person could use["pink*"]as a JavaScript key.variable.propertyallows constraining which property on the variable is usedvariable.fromallows constraining the file the variable is imported from. This is specified by the path relative to the ESLint config file; it is validated after resolving subpath imports and import aliasesConstants
{const: true}and{const: { name: "colors" }}behaves the same as variables, but forstylex.defineConsts()instead ofstylex.defineVars().CSS variables
{ cssVar: true }allowsvar()expressions. Passing an object allows constraining the name of the variable.CSS calculations
{calc: true}allowscalc()expressions. In this strawman proposal it does not accept further customization.null
{ null: true }allowsnullas a value (e.g. fordefaultcase in nested blocks)Remarks
This proposal is really several individual features; for my current use case I'd be quite happy with just being able to do
[{null: true}, {variable: true}].In the current implementation,
limit: nullis equivalent tolimit: {variable: true}since variable values are not validated at all; however, this is causing problems withdefault: nullin nested configs. I am getting around this by doingwhich is awkward.