vanilla-extract-css / vanilla-extract

Zero-runtime Stylesheets-in-TypeScript

Home Page:https://vanilla-extract.style

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Issue with selecting a child of a recipe

leo-petrucci opened this issue · comments

Describe the bug

I'm attempting to move away from styled-components and I think I've come across a bug.

In my codebase I have an outer container which can have multiple variants (primary, secondary, etc.). The element then has an internal element which has styles that depend on which variant the parent has.

In css it would be something like:

.button.primary .inner_container {
  // styles specific to the inner_container of a primary button
}

.button.secondary .inner_container {
  // styles specific to the inner_container of a secondary button
}

So in vanilla-extract I replicated it like this:

import { recipe } from '@vanilla-extract/recipes';
import { style } from '@vanilla-extract/css';

export const VanillaButton = recipe({
  base: {
    padding: '1rem',
    cursor: 'pointer',
  },

  variants: {
    variant: {
      primary: {
        background: 'red',
      },
    },
  },
});

export const VanillaButtonInternal = style({
  selectors: {
    [`${VanillaButton({ variant: 'primary' })} &`]: {
      color: 'blue',
    },
  },
});

I think this should work, however when I go check the generated css for VanillaButtonInternal in my browser it looks like this:

.mojo_VanillaButton__17vmkuy0 .mojo_VanillaButton_variant_primary__17vmkuy1 .mojo_VanillaButtonInternal__17vmkuy2 {
    color: blue;
}

I think vanilla extract should generate the class as .mojo_VanillaButton__17vmkuy0.mojo_VanillaButton_variant_primary__17vmkuy1 (without the space). Since it's trying to target VanillaButton with the primary variant, not VanillaButton with a child that has primary variant.

Please let me know if I've misunderstood how this works or if I'm not supposed to do this!

Checking in the reproduction:
You should be able to use the developer tools to inspect the button, which should then allow you to open the page.css file generated for that specific component.

It should look something like this:

/*!***************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\
  !*** css ./node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[14].oneOf[0].use[1]!./node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js??ruleSet[1].rules[14].oneOf[0].use[2]!./node_modules/@vanilla-extract/webpack-plugin/virtualNextFileLoader/dist/vanilla-extract-webpack-plugin-virtualNextFileLoader.cjs.js!./node_modules/@vanilla-extract/webpack-plugin/vanilla.virtual.css?%7B%22fileName%22%3A%22components%2FButton%2FButton.css.ts.vanilla.css%22%2C%22source%22%3A%22LkJ1dHRvbl9WYW5pbGxhQnV0dG9uX18ycWRvZWMwIHsKICBwYWRkaW5nOiAxcmVtOwogIGN1cnNvcjogcG9pbnRlcjsKfQouQnV0dG9uX1ZhbmlsbGFCdXR0b25fdmFyaWFudF9wcmltYXJ5X18ycWRvZWMxIHsKICBiYWNrZ3JvdW5kOiByZWQ7Cn0KLkJ1dHRvbl9WYW5pbGxhQnV0dG9uX18ycWRvZWMwIC5CdXR0b25fVmFuaWxsYUJ1dHRvbl92YXJpYW50X3ByaW1hcnlfXzJxZG9lYzEgLkJ1dHRvbl9WYW5pbGxhQnV0dG9uSW50ZXJuYWxfXzJxZG9lYzIgewogIGNvbG9yOiBibHVlOwp9%22%7D ***!
  \***************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
.Button_VanillaButton__2qdoec0 {
    padding: 1rem;
    cursor: pointer;
}

.Button_VanillaButton_variant_primary__2qdoec1 {
    background: red;
}

.Button_VanillaButton__2qdoec0 .Button_VanillaButton_variant_primary__2qdoec1 .Button_VanillaButtonInternal__2qdoec2 {
    color: blue;
}

Reproduction

https://stackblitz.com/edit/stackblitz-starters-vqe2d9?file=app%2Fpage.tsx

System Info

System:
    OS: Linux 5.0 undefined
    CPU: (6) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 0 Bytes / 0 Bytes
    Shell: 1.0 - /bin/jsh
  Binaries:
    Node: 18.18.0 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 10.2.3 - /usr/local/bin/npm
    pnpm: 8.15.3 - /usr/local/bin/pnpm
  npmPackages:
    @vanilla-extract/css: ^1.14.1 => 1.14.1 
    @vanilla-extract/next-plugin: ^2.3.7 => 2.3.7 
    @vanilla-extract/recipes: ^0.5.2 => 0.5.2

Used Package Manager

npm

Logs

No response

Validations

I did some digging and I think the issue comes from this line:

className += ' ' + selectionClassName;

I think that at the moment there's an assumption that css classes should look the same when they're printed in the html as when they're printed into CSS. In HTML it's fine to have the space, but a space between two classes means something else entirely in CSS. If this function is used for both situations it'll work for one but not the other.

Is there a way to understand which context createRuntimeFn is being run? i.e. to create classes for HTML or classes for CSS?

I know somewhere . gets stripped out of the output of that function, I just have no idea where 🤣

Let me know, I'd love to merge a fix if this is indeed a bug.

This is silly, but this is a much easier solution:

export const VanillaButtonInternal = style({
  selectors: {
    [`${VanillaButton.classNames.variants.variant.primary} &`]: {
      color: 'blue',
    },
  },
});