vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
47.46k stars 8.3k forks source link

CSS nesting inserts attribute selector at every level #10567

Closed KaelWD closed 1 month ago

KaelWD commented 7 months ago

Vue version

3.4.21

Link to minimal reproduction

https://play.vuetifyjs.com/#eNptkN1OAyEQhV+FzIXRxLJZf25WbGL6Ct65XlB2okQKCLPbmKbvLj+7pqa9gXMOzMcMbweIQTUv3vNpROhAEO68kYTr3jImppX0vshilLMktcUwRzWUYWCkyeBzD5tkXrPugSkjY0wZYaQe1iyfsU1CoCUmmlq5wLP9R89JfVw0J00lG+nHIIvKeazlfG7iUFlbqb4+ghvtkJDGhY5JS/p7xP2nJnyql7oB0V9fLbWrdLK7WQiXGFsz4qSdQZoJ54zyCSeQi5jg9vaPcKyibGkRTZktDQXHW7jnD/yuhSweedvC+y/pQ4mT

Steps to reproduce

Have a scoped style block with native css nesting

<style scoped>
  .v-card {
    background-color: antiquewhite;
    & .v-card-item {
      background-color: blueviolet;
      & .v-card-title {
        background-color: brown;
      }
    }
  }
</style>

What is expected?

.v-card {
  &[data-v-7ba5bd90] {
    background-color: antiquewhite;
  }
  & .v-card-item {
    &[data-v-7ba5bd90] {
      background-color: blueviolet;
    }
    & .v-card-title[data-v-7ba5bd90] {
      background-color: brown;
    }
  }
}

What is actually happening?

.v-card[data-v-7ba5bd90] {
  background-color: antiquewhite;
  & .v-card-item[data-v-7ba5bd90] {
    background-color: blueviolet;
    & .v-card-title[data-v-7ba5bd90] {
      background-color: brown;
    }
  }
}

System Info

No response

Any additional comments?

:deep needs to omit the selector for anything nested under it, so :deep(.v-card) should just be

[data-v-7ba5bd90] .v-card {
  background-color: antiquewhite;
  & .v-card-item {
    background-color: blueviolet;
    & .v-card-title {
      background-color: brown;
    }
  }
}
matthew-dean commented 7 months ago

Here's a similar / simplied example of this issue:

<style scoped>
div {
  display: grid;
  :deep(> *) {
    margin: 0;
  }
}
</style>

This should output:

div[data-v-d127f3cd] {
  display: grid;
  > * {
    margin: 0;
  }
}

Instead, it outputs:

div[data-v-d127f3cd] {
  display: grid;
  [data-v-d127f3cd] > * {
    margin: 0;
  }
}

(The latter of course represents an entirely different DOM structure.) I would expect :deep() to "escape" all scope re-writing, but it doesn't.

hugoattal commented 3 months ago

I think the attribute SHOULD BE inserted at every levels. If not, the :deep selector becomes useless.

But I think it should not cascade in the :deep selector. Here's my simple example that breaks with the :deep selector:

:deep(p) {
  color: red;
  strong {
    color: black;
  }
}

compiles to

[data-v-7ba5bd90] p {
  color: red;
  strong[data-v-7ba5bd90] {
      color: black;
  }
}

but I expected this to compile to

[data-v-7ba5bd90] p {
  color: red;
  strong {
      color: black;
  }
}

Temporary solution (thanks Hiws from the Vue Land discord server ❤️):

:deep(p) {
  color: red;
  :global(strong) {
    color: black;
  }
}
zewt commented 2 months ago

Running into this too. It seems like :deep just doesn't understand nested selectors, which is a bit of a showstopper. Putting :global around every nested selector in a complex stylesheet makes quite a mess...

KaelWD commented 2 months ago

I think the attribute SHOULD BE inserted at every levels

No, the equivalent non-nested CSS only inserts it at the last selector.

.v-card .v-card-item .v-card-title {}
/* --> */
.v-card .v-card-item .v-card-title[data-v-7ba5bd90] {}
.v-card {
  & .v-card-item {
    & .v-card-title {}
  }
}
/* --> */
.v-card[data-v-7ba5bd90] .v-card-item[data-v-7ba5bd90] .v-card-title[data-v-7ba5bd90] {}