Open Aaron-Pool opened 5 years ago
Hey @Aaron-Pool thanks for the feedback!
Agreed on 1 and 3, I've got an idea of how to nicely do #1 so I'll likely be doing that this week/weekend.
I like the idea of 2, having an option to enable scoping would be great and consistent with vue, I'll have to play around and see if I can figure out the best way to do that.
Sounds good! Let me know if you need a hand with anything. Been considering taking a crack a cleaner vue-emotion implementation for a while now. So I'm happy to pitch in if it helps move things along.
For the scoping, the expectation would be that the styles would be scoped to the elements in the same context that the styled component is used, and not leak into the “insides” of the styled component, right? Since css can’t go up, that means that it’s limited to slotted elements (default or otherwise).
So basically:
// Wrapper.vue
<template>
<div>
<span>Should not be red</span>
<slot />
</div>
</template>
// App.vue
<template>
<styled-wrapper>
<span>Should be red</span>
</styled-wrapper>
</template>
<script>
import Wrapper from './Wrapper.vue'
import styled from '@vue-emotion/styled'
const StyledWrapper = styled(Wrapper)` // somehow scoping enabled
span {
color: red;
}
`
export default {
components: {
StyledWrapper
}
}
</script>
I think because the applicable scope is basically the slots, it’s definitely doable, but I wanted to make sure my understanding of the use-case is correct?
@ParkerD559
I've been thinking about this, and I think it essentially boils down to the fact that, with the way people use the styled components paradigm, the Vue SFC <style>
block is typically eliminated, which removes the ability to specify <style scoped>
. I think the most straight forward approach to implementing it for styled components would actually be to have a function you can call on your component definition export to add a scope attribute to every createElement
call in the components render function (which would be compiled from the <template>
tag. Then, for you styled component, you could just say something like .withScope(*ParentComponentName*)
and it would append the [data-hash]
attribute selector to all of it's style rules, just like the vue scoped styles implementation does.
For reference: https://vue-loader.vuejs.org/guide/scoped-css.html#scoped-css
Possible api example
<script>
import styled from '@vue-emotion/styled';
import { scopedStyles } from '@vue-emotion/utils';
const ParentComponent = scopedStyles({
components: {
StyledWrapper
}
});
const StyledWrapper = styled(Wrapper)`
span {
color: red;
}
`.withScope(ParentComponent);
export default ParentComponent;
</script>
<template>
<styled-wrapper>
<span>Should be red</span>
</styled-wrapper>
</template>
Which would end up functioning as if it were:
<script>
import styled from '@vue-emotion/styled';
const ParentComponent = {
components: {
StyledWrapper
}
};
const StyledWrapper = styled(Wrapper)`
span[data-423sdfs] {
color: red;
}
`;
export default ParentComponent;
</script>
<template>
<styled-wrapper data-423sdfs>
<span data-423sdfs>Should be red</span>
</styled-wrapper>
</template>
Btw, I went ahead and redid how the styling "works" so it now uses functional wrappers instead of mixins and regular components, and now you can target styled components -- not yet with object styles though. Going to start experimenting with the scoping stuff
Great! Might play around with dropping it in place of the archived version of vue-emotion
, see if it breaks anything.
add a scope attribute to every createElement call in the components render function
After some experimentation this really isn't possible because the scoped attribute needs to be added after all children are done rendering as well, so it's only possible to add it to the top-most element.
But! I went looking through vue-compiler-utils
and vue/core/vdom
, and I've kinda figured out how to get the scoping to work. Usage would involve adding a plugin, and then passing an option to styled
, so:
import Vue from 'vue';
import { ScopedStyles } from '@vue-emotion/plugins';
Vue.use(ScopedStyles)
// App.vue
<template>
<styled-wrapper>
<span>Should be red</span>
</styled-wrapper>
</template>
<script>
import Wrapper from './Wrapper.vue'
import styled from '@vue-emotion/styled'
const StyledWrapper = styled(Wrapper, { scoped: true })`
span {
color: red;
}
`
export default {
components: {
StyledWrapper
}
}
</script>
It would mimic how Vue does its own scoping (via postcss plugin and during vdom patching) by using a stylis plugin and a custom directive added by the render function (which has a hook for when vnodes are done being made). I think this is good because it actually allows you to specify scoped styles outside of a SFC, which is not possible with Vue anyways!
Oh, I was actually talking about preprocessing the render function in the component definition as a string to add the scoping attributes (even a SFC has a render function generated from the given template), and then regenerating a new render function with the modified string
You're approach is perfectly sensible as well, I was just trying come up with a way to create the closest possible feature parity with <style scoped>
functionality, but perhaps the added complexity isn't worth it đź‘Ť
So I hit a wall trying to get this to work in the scoped branch, I got it to work with default slots but not named slots (which apparently are mounted after directives hooks are run...) so I'm fairly stuck on this haha
@ParkerD559 In Vue 2.6, all slot content became available on the $scopedSlots
context property. ScopedSlots are compiled at a different point in the render lifecycle, which may solve you problem.
For reference:
I've been using the (now archived) version of
vue-emotion
by egoist for a while now. The three things that would really make me switch to this library would be:1) an invisible implementation of targeting a child emotion component (but you already created an issue for that, so that's good đź‘Ť ) 2) Some sort of implementation that mimics scoped styles of vue style blocks. Because, at the point I am able to target emotion children components, I have to worry about styles leaking down through my component tree. 3) Switching to the styled wrapper component to be a functional component. I use these everywhere, so, the lighter the better.