vitejs / vite

Next generation frontend tooling. It's fast!
http://vitejs.dev
MIT License
67.44k stars 6.07k forks source link

CSS module prefix query parameter #18069

Open guoyunhe opened 2 weeks ago

guoyunhe commented 2 weeks ago

Description

In CSS module, we would like to write shorter class names:

/* ProductCard.module.css */
.root {}
.image {}
.button {}

However, when we monitoring online traffic and user interactions, we tends to record class names and ids. Short class names can be confusing. For example, many components may have .image:

.image_l2sjf {}
.image_xrfs1 {}
.image_xeqwk {}

It is hard to know which component the .image class name belongs to.

Suggested solution

I suggest to support a special query parameter prefix to add extra context for class names:

import styles from './ProductCard.module.css?prefix=ProductCard';

Which will generate css like this:

.ProductCard_root_af13s {}
.ProductCard_image_1irfj {}
.ProductCard_button_fseis {}

Alternative

According to this article, Create React App can automatically prefix css module class name with the file name:

image

Vite doesn't support this magic out-of-box, but you can achieve it by config:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  css: {
    modules: {
      generateScopedName: '[name]_[local]_[hash:base64:5]'
    }
  }
});

Additional context

No response

Validations

sapphi-red commented 2 weeks ago

Why do you think having a query parameter is better than using generateScopedName?

guoyunhe commented 2 weeks ago

Why do you think having a query parameter is better than using generateScopedName?

a query parameter allows you to handle complex situation, while generateScopedName is a static rule based on filepath etc. For example

// full path is src/order/product/index.module.css
import styles from './index.module.css?prefix=OrderProduct';

// full path is src/order/product/Cover.module.css
import styles from './Cover.module.css?prefix=OrderProductStatus';

Both query parameter and generateScopedName have their advantages.

sapphi-red commented 2 weeks ago

while generateScopedName is a static rule based on filepath etc.

You can use pass a function to generateScopedName.

generateScopedName: function (name, filename, css) {
  const map = { 'src/order/product/index.module.css': 'OrderProduct' }
  const mapped = map[normalize(filename)]
  const line = css.substr(0, i).split(/[\r\n]/).length;
  const file = path.basename(filename, ".css");
  return "_" + map + "_" + line + "_" + name;
},

I noticed now that importing a same file with different query would be a problem.

// src/foo.js
import styles from './index.module.css?prefix=OrderProduct';
// src/bar.js
import styles from './index.module.css?prefix=OrderProductStatus';