linaria icon indicating copy to clipboard operation
linaria copied to clipboard

styled.element props not working

Open js2me opened this issue 3 years ago • 2 comments

Environment

Windows 10, Webpack 5, node 14.18.3,

    "@linaria/babel-preset": "^4.1.2",
    "@linaria/core": "^4.1.1",
    "@linaria/react": "^4.1.2",
    "webpack": "^5.74.0",
    "webpack-bundle-analyzer": "^4.5.0",
    "webpack-cli": "^4.10.0",
    "webpack-closure-compiler": "^2.1.6",
    "webpack-dev-server": "^4.10.0"
    "@babel/core": "^7.18.10",
    "@babel/plugin-syntax-dynamic-import": "^7.8.3",
    "@babel/polyfill": "^7.12.1",
    "@babel/preset-env": "^7.18.10",
    "@babel/preset-react": "^7.18.6",
    "@babel/preset-stage-0": "^7.8.3",
    "@babel/register": "^7.18.9",
    "@linaria/webpack-loader": "^4.1.2",

Description

I create simplest component Typography with prop color which transformed into css variable via prop fn, but I've got problem that component received css class name (as I guess) but expect to be received (prop string value which I send to component as prop)

code

const FONT_WEIGHT = {
  normal: 500,
  bold: 600,
  bolder: 700,
};

const Typography = styled.span<{
  color?: Colors;
  weight?: keyof typeof FONT_WEIGHT;
  align?: CSSProperties['textAlign'];
}>`
  color: ${(props) => `var(--${props.color})`};
  display: block;
  letter-spacing: 0.01em;
  margin: 0;
`;

export const H1 = styled(Typography)`
  font-size: 42px;
  line-height: 54px;
`;

H1 usage

         <H1 color={'primary'}>
              example
            </H1>

image

postcss.config.js

/* eslint-disable @typescript-eslint/no-var-requires */

const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const sorting = require('postcss-sorting');

const isProd = process.env.NODE_ENV === 'production';

module.exports = {
  syntax: 'postcss-scss',
  plugins: [
    sorting(),
    autoprefixer,
    isProd &&
      cssnano({
        preset: [
          'default',
          {
            discardComments: {
              removeAll: true,
            },
          },
        ],
      }),
  ].filter(Boolean),
};

linaria.config.js

/* eslint-disable @typescript-eslint/no-var-requires */
const stylis = require('stylis');

const isProd = process.env.NODE_ENV === 'production';

stylis.set({ prefix: false, compress: isProd });

// https://github.com/callstack/linaria/blob/master/docs/CONFIGURATION.md
module.exports = {
  evaluate: true,
  displayName: !isProd,
};

MiniCssExtractPlugin

 new MiniCssExtractPlugin({
      filename: isProd ? 'styles-[contenthash].css' : 'styles.css',
      chunkFilename: "styles/[id].css",
      ignoreOrder: false,
    }),

css webpack rules

{
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          {
            loader: 'css-loader',
            options: { sourceMap: !isProd },
          },
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                config: path.resolve(__dirname, "postcss.config.js")
              }
            }
          }
        ],
      }

ts webpack rules

{
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
          },
          {
            loader: '@linaria/webpack-loader',
            options: {
              sourceMap: !isProd,
            },
          },
          {
            loader: "ts-loader",
            options: {
              transpileOnly: false,
            },
          },
        ],
      }

babel.config.js

module.exports = function (api) {
  api.cache.never();

  return {
    sourceMaps: true,
    presets: [
      "@babel/react",
      [
        "@babel/env",
        {
          useBuiltIns: "usage",
          corejs: {
            version: 3,
          },
        },
      ],
      "@linaria"
    ],
    plugins: [
      "babel-plugin-styled-components",
      "react-hot-loader/babel",
      [
        "babel-plugin-module-resolver",
        {
          alias: {
            "effector": "./src/store/__internal__/domain",
            "@origin-effector": "./node_modules/effector/effector.cjs.js",
            ...(process.env.NODE_ENV === "production" ? {
              "effector-logger": "./src/store/__internal__/effector-logger",
            } : {}),
          }
        }
      ],
      process.env.NODE_ENV === "development" && [
        "effector-logger/babel-plugin",
        {
          inspector: false,
          effector: {
            addLoc: true,
            addNames: true
          }
        }
      ],
    ].filter(Boolean),
    env: {
      development: {
        plugins: process.env.DEV_SERVER ? ["react-refresh/babel"] : [],
      },
    },
  };
};

js2me avatar Aug 19 '22 23:08 js2me

Or it is not possible with this tool ?

js2me avatar Aug 20 '22 00:08 js2me

I'm guessing you need to specify the configFile option to the babel-loader and as babelOptions to the Linaria webpack loader. See my comment here: https://github.com/callstack/linaria/issues/589#issuecomment-1224287054

anulman avatar Aug 28 '22 22:08 anulman

Hi @js2me

For property based interpolations Linaria uses css variables. In your case, var(--${props.color}) will be compiled as var(--var(--some-generated-name)) that is just an invalid CSS syntax. There are at least two ways to address you problem:

  1. interpolate not a variable name, but a color: color: ${(props) => NameToValue[props.color]};;
  2. use cx to add a class name with the specific color https://tsplay.dev/ND2eOW

Anber avatar Oct 17 '22 11:10 Anber