Linaria: why don't use sort class name like "a b c"

Created on 10 Oct 2020  路  19Comments  路  Source: callstack/linaria

Hi, I just wonder why don't generate class name like a b c d? why must a random hash 5 length? More sort is more light css file, right?

proposal 馃挰 triage 馃彿

All 19 comments

Hi @IRediTOTO
With short names there are more chances of collisions between names. Sequential names will cause renaming throw the whole project if the original order of building is changed (new file is added between component A and component B -> all class names after "a" will be shifted).
However, if it's not a problem in your project you can easily override this behavior (classNameSlug in a config).

I'm sorry to up this topic. By maybe @IRediTOTO wanted something like this:
linaria.config.js

function isProduction() {
    return process.env.NODE_ENV === 'production'
}

function createCssClassNameSequence() {
    const alphabet = 'abcdefghijklmnopqrstuvwxyz'
    const alphabetLength = alphabet.length
    let index = 1

    function numberToChars(number) {
        let chars = ''

        while (number > 0) {
            let n = (number - 1) % alphabetLength
            chars = alphabet[n] + chars

            number = Math.floor((number - n) / alphabetLength)
        }

        return chars
    }

    return function() {
        const chars = numberToChars(index)
        index++

        return chars
    }
}

const getNextCssClassName = createCssClassNameSequence()

module.exports = {
    displayName: true,
    classNameSlug(hash, title) {
        // if development mode it will return class name like `.myComponent`
        if (!isProduction()) {
            return title
        }

        // else it will generate classes a, b, c, ..., z, aa, ab, ...
        return getNextCssClassName()
    }
}

@ym-project I will try in next project. Thank you.

@ym-project this make some css lost => layout broken. Do you have any idea? Do you usually use this?

@ym-project this make some css lost => layout broken. Do you have any idea? Do you usually use this?

I used it once. And didn't notice problems. We use legal linaria API to transform class names. If you have problems with short names I think you will have problems with original class names too.

@Anber
I'm sorry to mention you. Can I face some problems if class names will be named as above (comment link)?

Hi @ym-project
Yep. At least classNameSlug should return stable value for each pair of hash and title. You can add a cache or just use something like lodash.memoize.

if (!isProduction()) {
    return title
}

It also can cause problems if two or more components have the same title. You should use hash as a part of a name.

I am using mangle-css-class-webpack-plugin, it work pertty good but I must have prefix name for every class.
@ym-project your worked, but it just lost some css in production
why don't just simple put classname follow alphabet a b then c ... aa ab ... aac...

@Anber I got your idea. Thank you for explain.

if (!isProduction()) {
  return title
}

It also can cause problems if two or more components have the same title. You should use hash as a part of a name.

Agree.

Hi @ym-project
Yep. At least classNameSlug should return stable value for each pair of hash and title. You can add a cache or just use something like lodash.memoize.

Could you give me cases when I will get problems? I tried the same class names in two components
App.js

const App = () => (
    <div className={app}>
        <Button />
    </div>
)
const app = css`....`

Button.js

const Button = () => (
    <button className={app}>Button Text</button>
)
const app = css`....`

and got other names after bundle

<div id="root">
    <div class="a">
       <button class="b">Button Text</button>
    </div>
</div>

I tried another several cases and It works great every time.

I am using mangle-css-class-webpack-plugin, it work pertty good but I must have prefix name for every class.
@ym-project your worked, but it just lost some css in production
why don't just simple put classname follow alphabet a b then c ... aa ab ... aac...

I'm sorry I never used this plugin. But with config like this https://github.com/ym-project/linaria-babel-bug/blob/master/webpack.config.js it's ok.

Could you give me cases when I will get problems?

classNameSlug can be called two or more times for the same class: once during transpile stage and multiple time during the evaluation stage.

Let's see an example:
classes.js

export const activeClass = css`鈥;

Component.jsx

import { activeClass } from "./classes.js";

export const Component = styled.div`
&.${activeClass} {
  color: red;
}
`

The first call of classNameSlug will be for activeClass during transpile stage. The second will be for Component. And there will be the third one for activeClass during the dependency evaluation stage because activeClass is used as an interpolated value and should be evaluated.

@Anber ok, I see. But I don't understand the problem still. Described by you behavior suits for default naming and custom naming + hash and just custom naming like below (a, b, ...).

classNameSlug(hash, title) {return hash + '__' + title}

<div id="root">
    <div class="am0as6w__app by6ys2b__blueBG">
        <button class="a5lf8hy__app">
            <button class="s1btlqlp__SubButton">
                <div class="by6ys2b__blueBG">hello</div>
            </button>
        </button>
    </div>
</div>

default

<div id="root">
    <div class="am0as6w by6ys2b">
        <button class="a5lf8hy">
            <button class="s1btlqlp">
                <div class="by6ys2b">hello</div>
            </button>
        </button>
    </div>
</div>

classNameSlug(hash, title) {return getNextCssClassName()}

<div id="root">
    <div class="a d">
        <button class="c">
            <button class="b">
                <div class="d">hello</div>
            </button>
        </button>
    </div>
</div>

All cases have the same behavior.

@ym-project https://codesandbox.io/s/linaria-demo-forked-om4ws?file=/src/App.js an example of broken behaviour

Despite the fact that <Title className={activeClass}> has been transpiled to <h1 class="seq_4 seq_1">, the css for Title looks like .seq_1{font-family:sans-serif;font-size:48px;color:blue;}.seq_1.seq_3{color:red;}. It happens because classNameSlug is called many times for the same component/class and in your implementation it always returns next class name.

But you are right that this behaviour is extremely unclear and we should memoize classNameSlug on our side.

Can we save prev class name, then if run to another file we will know what class name should be. If make class name increase it will not conflict.
Ex file css A have class a b c save c name
Then in file B we know c so lets run from d???

Something like that should work

function isProduction() {
    return process.env.NODE_ENV === 'production'
}

function createCssClassNameSequence() {
    const cache = new Map()
    const alphabet = 'abcdefghijklmnopqrstuvwxyz'
    const alphabetLength = alphabet.length
    let index = 1

    function numberToChars(number) {
        let chars = ''

        while (number > 0) {
            let n = (number - 1) % alphabetLength
            chars = alphabet[n] + chars

            number = Math.floor((number - n) / alphabetLength)
        }

        return chars
    }

    return function(longName) {
        if (!cache.has(longName)) {
            const chars = numberToChars(index)
            index++
            cache.set(longName, chars)
        }

        return cache.get(longName)
    }
}

const getNextCssClassName = createCssClassNameSequence()

module.exports = {
    displayName: true,
    classNameSlug(hash, title) {
        const longName = `${hash}_${title}`
        // if development mode it will return class name like `.myComponent`
        if (!isProduction()) {
            return longName
        }

        // else it will generate classes a, b, c, ..., z, aa, ab, ...
        return getNextCssClassName(longName)
    }
}

@ym-project https://codesandbox.io/s/linaria-demo-forked-om4ws?file=/src/App.js an example of broken behaviour

Despite the fact that <Title className={activeClass}> has been transpiled to <h1 class="seq_4 seq_1">, the css for Title looks like .seq_1{font-family:sans-serif;font-size:48px;color:blue;}.seq_1.seq_3{color:red;}. It happens because classNameSlug is called many times for the same component/class and in your implementation it always returns next class name.

aaaaah... Now I understand! :)

@Anber It work PERFECT, thank you so much. This should be build in feature.

Was this page helpful?
0 / 5 - 0 ratings