emotion-js / emotion

👩‍🎤 CSS-in-JS library designed for high performance style composition
https://emotion.sh/
MIT License
17.47k stars 1.11k forks source link

Nesting Emotion 10 classes for cascade #1217

Closed BMCwebdev closed 5 years ago

BMCwebdev commented 5 years ago

Previously in Emotion 9 you were able to use Emotion class names to take advantage of cascade. You would wrap the emotion const in curly brackets and prefix it with a period, and then dollar sign. For example, you could do this:


const child = css`
  color: green;
`;
const parent = css`
  color: red;
  .${child} {
    color: yellow;
  }
`;

<div className={parent}>I am red</div>
<div className={child}>I am green</div>
<div className={parent}>
  <div className={child}>I am yellow</div>
</div>

How can I go about achieving this behavior in Emotion 10? That is my question.

The following is further information about what happens when you don't use a period-dollar sign.

Now, the following was and is desirable: if no period are used in Emotion 9 or 10, the parent const will inherit the nested const styles. And furthermore, if that nested const then has overriding styles, those would ultimately be inherited by the parent.

const child = css`
  color: green;
`;
const parent = css`
  color: red;
`;
<div className={parent}>I am red</div>
<div className={child}>I am green</div>

const child = css`
  color: green;
`;
const parent = css`
  ${child}
`;
<div className={parent}>I am green</div>

const child = css`
  color: green;
`;
const parent = css`
  ${child} {
    color: yellow;
  }
`;
<div className={parent}>I am yellow</div>

const child = css`
  color: green;
`;
const parent = css`
  color: red;
  ${child}
`;
<div className={parent}>I am green</div>

const child = css`
  color: green;
`;
const parent = css`
  color: red;
  ${child} {
    color: yellow;
  }
`;
<div className={parent}>I am yellow</div>
BMCwebdev commented 5 years ago

The following will work, it just doesn't work in CodePen as the output code there has more text tacked onto it, and it hashes the const names all over again. But if you test in your own code base, this does work. I don't like doing it, it feels hacky. Would like Emotion to have something specific for these instances.


const cat = css`
  color: red;
`;
const dog = css`
  color: green;
  .css-${cat.name} {
    border-bottom: 1px solid currentColor;
  }
`;
BMCwebdev commented 5 years ago

This isn't totally safe, mind you. If you are rendering subcomponents, they will not always get the same hashed class name. Additionally it also seems that Emotion will tack on the subcomponents name to the end of the hashed class name to avoid clashing I assume. So, proceed with caution. What I would really like is for Emotion to return the functionality it had in V.9 - an example of which is found in their V9 documentation, last example on this page:

https://5bb1495273f2cf57a2cf39cc--emotion.netlify.com/docs/nested

brandonkal commented 5 years ago

Yes, I agree this is needed! We can target with styled, but then composition is not as easy.

TerenceZ commented 5 years ago

Workaround: use pre-generated name and object css.

const child = css`
  color: green;
`;

const parent = css({
  color: 'red',
  [`.${child}`]: {
    color: 'yellow',
  },
});
BMCwebdev commented 5 years ago

I'm trying your suggestion, @TerenceZ , but I am not having any luck. The nested child styles do not appear to work.

FezVrasta commented 5 years ago

If you use the /macro or Babel preset you can reference other styled components in your selectors.

const Foo = styled.div``;
const bar = styled.div`
  ${Foo} {
    color: red;
  }
`;

If you want to use the syntax posted in your original post, you must make sure to use the emotion package, not the React-specific @emotion/css or @emotion/styled ones, because they don't directly generate class names, but objects not meant to be consumed by 3rd party code.

I hope this answers your question!

thibaultboursier commented 4 years ago

@FezVrasta @mitchellhamilton Could this breaking change be referenced in the Emotion documentation? I have not seen anything written on it. On the page about migration, we could add a text explaining the need to use the babel-plugin-macros to keep "styled children target" work.

Andarist commented 4 years ago

@thibaultboursier being able to use styled components as selector has always been a babel-powered feature

thibaultboursier commented 4 years ago

@Andarist Sure (https://emotion.sh/docs/styled#targeting-another-emotion-component). But we could add some information to say explicitly that it does not work anymore with Emotion 10.

Andarist commented 4 years ago

This should very much work with Emotion 10. Maybe you experience some misconfiguration problem? Could you prepare a repro case on which I could take a look?

lxsmnsyc commented 4 years ago

is there any solution to this? I am trying to achieve something like

const A = css`
// ...some style
`;

const B = css`
  // some style
  .${A} & {
    // some update style
  }
`;

but it seems that it doesn't work at all

ksweetie commented 4 years ago

@LXSMNSYC Does this work for you? It seems hacky and is recommended against above in this thread.

const A = css`
// ...some style
`;

const B = css`
  // some style
  .css-${A.name} & {
    // some update style
  }
`;
lxsmnsyc commented 4 years ago

@ksweetie not with the emotion package, it returns a string

gbrocha commented 3 years ago

Some update?

BMCwebdev commented 3 years ago

I don't think this is going to be changed. And the way I work with Emotion now, I wouldn't need it anymore. What is the use case you are trying to solve for @rochagbr ?

thomasryu commented 3 years ago

I also would like support for this feature. Coming from Emotion 9, having to update my entire CSS because of the drop in support for this is a HUGE pain. Styled components and standard CSS support nested selectors so why can't this as well?

The most common use case for me is when a parent component changes state, which affect the style of its children. Instead of adding a new class to every single one of them, I just have to add it to said parent, and select these children using nested selectors. As a plus, all the style changes are grouped up in one place in the code, which eases visibility.

DanielMartin96 commented 3 years ago

Hello @BMCwebdev,

How would you write this code? I have nested classes in scss and not sure how to write it out in emotion.

.site-header {
  padding: $spacing-m 0;
  position: relative;
  z-index: 10;
  background: $white;

  .site-signin {
    line-height: 1.4rem;
    color: $color-interface-light;
    text-transform: capitalize;
    background-color: $white;
    box-sizing: border-box;
    border-radius: 7rem;
    position: absolute;
    top: 1.6rem;
    left: 1.6rem;
    text-decoration: none;
  }
  .site-myaccount {
    font-family: $text-font-ef;
    background-color: $white;
    border: none;
    position: absolute;
    top: 1.6rem;
    left: 1.6rem;
    font-size: 0;
    &::after {
      content: attr(data-username);
      color: $white;
      font-size: 1.6rem;
      background-color: $crest-blue;
      border-radius: 7rem;
      width: 3.4rem;
      height: 3.4rem;
      display: inline-block;
      line-height: 3.4rem;
      text-align: center;
    }
  }

How do you work with emotion now?

Any help is much appreciated!

Thanks, Dan

Mosquid commented 2 years ago

Any update on this?

leohxj commented 2 years ago

Is there a doc reference for add .css- prefix?

srmagura commented 2 years ago

There are a few ways to handle this in the latest version of Emotion (Emotion 11):

import { css } from '@emotion/react'

const myCss = css`
  div {
    p {
      color: red;
    }
  }
`

If none of these solutions help you and you think there is a need for a new feature to support this, please open a new issue.

ArielTzentner commented 2 years ago

Any update?

avshyz-gs commented 2 years ago

Silently breaking the api, and removing support of existing features, is incredibly frustrating.

norskeld commented 2 years ago

I was lurking for a solution as well and ended up using emotion's labels feature and CSS attribute selectors. It's not pretty, but it works, and it's not as fragile, as that trick with .css-${style.name}.

import { css } from '@emotion/react'

const parentCss = css`
  color: red;

  [class$="child"] {
    color: yellow;
  }
`

const childCss = css`
  label: child;
  color: green;
`

export default function Preview() {
  return (
    <>
      <div css={parentCss}>I am red</div>
      <div css={childCss}>I am green</div>
      <div css={parentCss}>
        <div css={childCss}>I am yellow</div>
      </div>
    </>
  )
}
avshyz-gs commented 2 years ago

Thanks for the take, @norskeld! Though, I'm afraid this solution is also a bit flaky - in a real project, with a substantial component tree, naming collisions are inevitable (how many false positives will this selector class$="child" catch?)

I'm surprised this thread is closed and disregarded :\

RZsam commented 2 years ago

is there any way to style class inside another element/class in object css?

WalterWeidner commented 1 year ago

Thanks for the take, @norskeld! Though, I'm afraid this solution is also a bit flaky - in a real project, with a substantial component tree, naming collisions are inevitable (how many false positives will this selector class$="child" catch?)

I'm surprised this thread is closed and disregarded :\

I am also really surprised by the sparse documentation in this Emotion provides on this topic. Especially, because many of those consuming this project are transitioning from CSS or SASS into CSS-in-JS solutions like Emotion.