question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Feature] Generate sub-classes to reduce duplication of CSS from dynamic prop combinations

See original GitHub issue

Have some way to split styles into sub-classes for dynamic props, rather than duplicate the entire CSS/style for each new variant from combining different dynamic prop values.

A user on the spectrum group recently posted about using CSS Grid to build a type of Table component.

They had dynamic props for the table cells based on row and column which made every cell unique. 25 rows resulted in 200+ classes, and they claimed it could go into the thousands of classes in some cases. If classes were split by dynamic props(perhaps with some helper to mark/enable), it’d be considerably less. Their workaround was similar to what I’ve shown below.

What will it allow you to do that you can’t do today?

Reduced class generation from unique dynamic prop combinations.

Each unique class variant duplicates all static CSS rules(as well as all vendor prefixed ones?).

Perhaps it’s considered a non-issue for SSR with gzip, or on client-side as it’s generated on demand and may use little memory or cause little performance hit from having hundreds of variants for what could be only a few classes instead?

How will it make current work-arounds straightforward?

I wouldn’t need to handle it externally/manually? Or bother with coming up with classnames? (One of the pros styled-components already promotes).

What potential bugs and edge cases does it help to avoid?

Should reduce memory from massive CSS duplication(static) from single prop changes introducing new dynamic variants/combinations. Possibly translates into better perf for the CSS parser in the browser?

No real-world bugs/edge cases come to mind.

DX wise, you’re forced into all styles being flattened/merged, despite any use of extension(styled(component)). In certain components, during development at least it’d be nice to maintain style/class separation(eg in dev tools, a component extends a button which has had it’s styles “reset”, followed by the extended button applying actual CSS the dev is interested in working with.

Example

The style string / CSS pre-generated::

const ICON_WIDTH = 80;
const ICON_HEIGHT = 80;

const backgroundImage = (output, [key, filepath]) => output + `
&.${key}{
  background: url(${filepath}) no-repeat;
}
`
const backgroundPosition = (output, [key, data]) => output + `
&.${key}{
  background-position: -${data.x}px -${data.y}px;
}
`
const generateCSSClasses = (dataObj, reducer) => Object.entries(dataObj).reduce(reducer, "")

const SkillStyles = `
  // common, base CSS (static)
  width: ${ICON_WIDTH}px;
  height: ${ICON_HEIGHT}px;
  background:red;
  position: absolute;

  // spritesheet classes - static after generation
  ${generateCSSClasses(spritesheets, backgroundImage)}

  // skill classes - static after generation (high amount)
  ${generateCSSClasses(IMG_SKILL_DATA, backgroundPosition)}

  // Toggle opacity - dynamic toggle based on component prop, effectively static
  // prop is only boolean, not a range value
  opacity: 0.2;
  &.isActive {
    opacity: 1;
  }
`

My styled component (or you can use regular div and assign the style to css prop and handle className prop there too):

```JS
const Icon = styled.div.attrs(props => ({
className: `${props.spritesheet} ${props.icon}${props.isActive ? ' isActive' : ''}`
})
)`${SkillStyles}`

Usage:

<Styled.Icon
          icon={icon}
          spritesheet={spritesheet}
          isActive={isActive}
/>

For 5 spritesheets with 20 icons each, that’s 100 unique classes(when only the background-position prop is unique per icon), and the opacity toggle state can double that to 200 unique classes, rather than be it’s own toggle class(scoped to the component), it doubles the variants with all that CSS duplicated. Above snippets don’t have that issue.

background-position could be made inline via attrs() instead, true. Then with the current styles, you’d only have the 5 spritesheets * 2(opacity states) = 10 unique classes(along with all the duplicated static CSS). Above snippet (which is obviously not a great solution/workaround, or even an option in some cases), without the background-position classes is only 7 unique classes (base + spritesheets(5) + opacity toggle(2)), no duplicated CSS(besides opacity) and clear separation in dev tools.

Problem

Have to create own css classes and add them manually via classNames, they also need to be prefixed with &. Would be great to use without attempting to generate all variants and their classes up front statically, and benefit from opacity: ${props => (props.isActive ? 1 : 0.2)}; usage, but having the scoped class generated like the main styled components one is.

Related Issues(?):

https://github.com/styled-components/styled-components/issues/220 https://github.com/styled-components/styled-components/issues/2069 https://github.com/styled-components/styled-components/issues/986 https://github.com/styled-components/styled-components/issues/937 https://github.com/styled-components/styled-components/issues/779


https://github.com/styled-components/styled-components/issues/1859 They express that they want to adjust a property for a component, but because variants duplicate all CSS for that component due to a different dynamic prop combination, you’re unable to adjust static CSS in dev tools. My above example has base CSS that’s common across all components, I ran into a similar issue.


https://github.com/styled-components/styled-components/issues/1209 I think this is pretty much covered via the css helper, but shows two approaches(one in the issue and another in related issue for compose method). If there was a method or way to define the class separation via array of tagged template literals(or via css helper or similar if necessary?), that’d be nice. Not only are single dynamic props declared for their own class but groups of props/rules could belong to their own class(afaik, a hash for a styled-component classname is derived from the given CSS output?, should be applicable to these groups/subclasses?)


https://github.com/styled-components/styled-components/issues/948 Could technically apply to this issue, by splitting the vendor prefixes added into their own class maybe(though with upcoming toggling on/off for that, I guess that’s less relevant)


https://github.com/styled-components/styled-components/issues/263

The amount of CSS you inject is pretty irrelevant, while there is a “maximum” it’s so huge that you should never be able to reach that point. Well that’s probably much easier to reach with highly dynamic styles when a single prop introducing a variant in a style full of other dynamic props duplicates all CSS including the static CSS for that component?


https://github.com/styled-components/styled-components/issues/134 Sort of related, shows perf issues from generating many classes, but is mostly due to high frequency updates to props, such as the colour picker example in the issue which is better suited for inline styles.


https://github.com/styled-components/styled-components/issues/1056 Suggested using prop types for static generation of variants upfront where possible.


https://github.com/styled-components/styled-components/issues/1018

this inline styles actually an issue for us especially when we running the web site in cloud and they charge by egress network data size. we have a unzipped 1.5MB html page and 1.3MB styles inline, it is gzipped as 200KB but it is still too large.


https://github.com/styled-components/styled-components/issues/940 https://github.com/styled-components/styled-components/issues/1530

However, we also support styled(Text) which creates two styledcomponents and two classes. Maybe so? It seems that this was a way for users in v3 to get the type of class separation, but in v4, a component ignores the separation and merges/flattens the classes into one.

https://github.com/styled-components/styled-components/issues/2104

Yes, this is intentional. Internally we now “fold” styled-components into each other when extended with styled(Comp) so that there’s less components in your hierarchy and less styles duplicated.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:4
  • Comments:5 (1 by maintainers)

github_iconTop GitHub Comments

2reactions
clovis1122commented, Mar 17, 2019

My use case is sightly related to this issue but it’s more about reducing CSS duplication than Class duplication.

I’m using the following component:

export const Title = styled.h1<{secondary?: boolean}>`
  font-size: 2.4rem;
  font-weight: bold;
  color: ${({ theme, secondary }) => secondary ? theme.colors.white : theme.colors.black};
  margin: 8px 0;
`;

Currently, Styled Components generates two class names where each has the same properties but differ in the color. I would expect one class with all the static properties, and one for each dynamic property:

.title {
  font-size: 2.4rem;
  font-weight: bold;
  margin: 8px;
}
.title-white {
  color: white;
}
.title-black {
  color: black;
}
0reactions
mieszko4commented, Aug 28, 2020

I’ve added another issue regarding this with explanation of my problem: https://github.com/styled-components/styled-components/issues/3240

Read more comments on GitHub >

github_iconTop Results From Across the Web

Basics - styled-components
No class name bugs: styled-components generates unique class names for your styles. You never have to worry about duplication, overlap or misspellings.
Read more >
Delightful ways to write reusable CSS using subclasses
Changes to the HTML will break the styles. They're not reusable. The module can't be reused in different locations without broken styles. Non-reusable...
Read more >
Sass: @extend
When @extend interleaves complex selectors, it doesn't generate all possible combinations of ancestor selectors. Many of the selectors it could generate are ...
Read more >
Classes | Webflow University
(When a class is duplicated, it BRINGS all the styles from the ORIGINAL class, but that's IT.) The relationship stops there. These are...
Read more >
Can a CSS class inherit one or more other classes?
There are tools like LESS, which allow you to compose CSS at a higher level of abstraction similar to what you describe.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found