lxsmnsyc / solid-styled

Reactive stylesheets for SolidJS
MIT License
173 stars 10 forks source link

Composition of common rules #33

Open xypnox opened 2 months ago

xypnox commented 2 months ago

In several instances, I repeat a set of css styles, for example for a button I will have a set of css rules which will also be used for a anchor tag as well. Ex:


const commonStyles = `
  padding: 1em;
`

const Button = () => {
  css` button { ${commonStyles} }`
  return <button></button>
}

const AButton = () => {
  css` a { ${commonStyles} }`
  return <a></a>
}

However, trying to accomplish this with solid-styled I run into the problem that the inserted string is converted to a variable.

Note that I will also have some specific styles in the css (not shown in example) so using a dynamic component doesn't make sense. (Ex: sharing border and focus styles between input and textarea, but adding additional styling in the respective components themselves)

Is there a way to compose styles (or insert style strings inside the css)?

lxsmnsyc commented 2 months ago

This is mentioned in limitations: https://github.com/lxsmnsyc/solid-styled?tab=readme-ov-file#limitations

Dynamic styles are only limited to CSS properties.

I'm still looking for a proper way to actually do this. The reason this is impossible currently is because all parts of the template string are converted to css variables reads (aka var(my-var)). Covering for other ways of inserting arbitrary string will require some advanced parsing capability, and at some point it maybe borderline to cover all possible use cases.

For actual composition, perhaps you can fallback into using normal CSS classes (or in some cases, data attributes) and style them globally.

xypnox commented 2 months ago

@lxsmnsyc Thanks for the clarification.

I really wanted this composition system. It would really benefit from the power of composing the strings with JS.

For example:

const generateClass = (cls: string) => `
  &.${cls} {
    color: var(--${cls}-o);
    border-color: var(--${cls}-b);
    background-color: var(--${cls}-b);
    &:hover {
      border-color: var(--${cls}-s-6);
      background-color: var(--${cls}-s-6);
    }
    &:active {
      border-color: var(--${cls}-s-4);
      background-color: var(--${cls}-s-4);
    }
    &:focus {
      outline-color: var(--${cls}-b);
    }
  }
`

css`
  button {
      ${['primary', 'secondary', 'tertiary'].map(generateClass).join('\n')}
  }
`

Looking at the converter code I guess adding some special syntax to denote strings as completely static would work?

https://github.com/lxsmnsyc/solid-styled/blob/42f9a051440051d8f666db57a42b4298fb4a5991/packages/solid-styled/compiler/core/process-css-template.ts#L12-L42

Ex:

if (t.isExpression(expr)) {
  if (!staticSyntax(expr)) {}
}

However, I am not sure which syntax would be the best one in this case. I propose for a double $$ for expressions which will be completely static and not be checked for variables.

Ex:

css`
  button {
    width: ${props.width};
    color: $${staticExpressionStr};

    $${longStaticCssStr}
  }
`

The converter would have to remove the preceding $. Moreover, this syntax doesn't break the highlighting in by editor (neovim).

I can draft a implementation if the syntax is okay?

lxsmnsyc commented 2 months ago

POCs are always welcome, however I would like to point out (and warn) that the ideal feature you would want requires a ton of work, that at some point it might not be worth the effort.