🐛 BUG: Scoped Styles and PostCSS don't work together
What version of astro are you using?
1.0.0-beta.27
Are you using an SSR adapter? If so, which one?
N/A
What package manager are you using?
npm
What operating system are you using?
Linux
Describe the Bug
I've noticed when using PostCSS that Scope Styles don't fit the workflow. When Astro components use Scope Styles, they seem to bypass whatever PostCSS pipeline is implemented. Aside from missing out on whatever optimizations you have set up, it also causes problems when using plugins that implement things like mixins and variables.
I don't know if there would be a way to address it, but Vue has a pretty nice implementation working with PostCSS. Maybe similar can be done?
Link to Minimal Reproducible Example
N/A
Participation
- [ ] I am willing to submit a pull request for this issue.
Astro should be running all your styles through PostCSS before scoping them—these were designed to work together.
If you have a specific use case that isn't working, please provide a reproduction!
Hey @natemoo-re. Thanks for the response.
I can give you a very simple example...
Set a variable through PostCSS (and its official PostCSS Simple Variables plugin), and try to use the variable within a component as a Scoped Style. Vite should ack with immediately saying it can't find the variable.
For a similar error, you can do the same for the Mixins plugin (or the Stylelint & PostCSS reporter plugin combo; however, with this example, you'll not get any output (from Vite) as if there was no input checked).
This leads me to believe that the Scoped Styles aren't being run through PostCSS as intended (if that's the case).
@natemoo-re Never really got a response on this. 🥺
Here's an example (with the official PostCSS Mixins plugin), if you still need it. It's a pretty simple demo really, as I've said...
This works.
src/styles/mixins.css
...
@define-mixin transition-cubic {
transition: all 0.5s ease;
transition-timing-function: cubic-bezier(0.86, 0, 0.07, 1);
transition-duration: 700ms;
}
...
src/styles/style.css
...
@import "mixins.css";
...
.navbar {
width: 100%;
padding: 0;
position: absolute;
left: 0;
top: 0;
z-index: 3;
.container {
flex-direction: column;
.upper-side {
width: 100%;
display: flex;
flex-wrap: wrap;
padding: 20px 0;
align-items: center;
transform: translateY(-80px);
@add-mixin transition-cubic;
transition-delay: 1.2s;
.logo {
margin-left: 0;
img {
height: 48px;
}
}
.phone-email {
margin-right: 0;
margin-left: auto;
margin-top: 5px;
text-align: right;
color: #fff;
img {
float: right;
height: 34px;
margin-left: 15px;
}
h4 {
margin-bottom: -5px;
margin-top: 3px;
font-size: 17px;
font-weight: 600;
line-height: 1;
}
small {
line-height: 1;
a {
opacity: 0.5;
color: #fff;
&:hover {
text-decoration: none;
color: $color-light;
opacity: 1;
}
}
}
}
.language {
margin-right: 40px;
margin-left: 60px;
padding: 10px 0;
padding-right: 30px;
border-right: 1px solid rgba(255, 255, 255, 0.15);
a {
display: inline-block;
margin: 0 5px;
color: #fff;
font-weight: 600;
&:hover {
color: $color-light;
text-decoration: none;
}
}
}
.hamburger {
width: 30px;
height: 21px;
margin-right: 0;
}
}
.menu {
width: 100%;
display: flex;
flex-wrap: wrap;
border-top: 1px solid rgba(255, 255, 255, 0.15);
@add-mixin transition-cubic;
transition-delay: 1.4s;
opacity: 0;
ul {
display: flex;
flex-wrap: wrap;
margin-left: auto;
margin-right: 0;
margin-top: -1px;
li {
margin: 0;
margin-left: 40px;
padding: 0;
list-style: none;
position: relative;
&:hover ul {
top: 100%;
opacity: 1;
visibility: visible;
}
ul {
min-width: 220px;
position: absolute;
left: -35px;
top: 120%;
background: $color-dark;
margin: 0;
padding: 25px 0;
opacity: 0;
visibility: hidden;
@add-mixin transition;
&:before {
content: "";
width: 0;
height: 0;
border-style: solid;
border-width: 0 10px 10px 10px;
border-color: transparent transparent $color-dark
transparent;
position: absolute;
left: 35px;
top: -10px;
}
li {
margin: 0;
padding: 0 35px;
white-space: nowrap;
a {
padding: 8px 0;
&:hover {
border-color: transparent;
}
}
}
}
a {
color: #fff;
display: inline-block;
font-weight: 600;
padding: 25px 0;
border-top: 1px solid transparent;
&:hover {
text-decoration: none;
color: $color-light;
border-top: 1px solid $color-light;
}
}
}
}
}
}
}
This does not work.
src/styles/mixins.css
...
@define-mixin transition-cubic {
transition: all 0.5s ease;
transition-timing-function: cubic-bezier(0.86, 0, 0.07, 1);
transition-duration: 700ms;
}
...
src/styles/style.css
...
@import "mixins.css";
...
src/components/Navbar.astro
...
<style>
.navbar {
width: 100%;
padding: 0;
position: absolute;
left: 0;
top: 0;
z-index: 3;
.container {
flex-direction: column;
.upper-side {
width: 100%;
display: flex;
flex-wrap: wrap;
padding: 20px 0;
align-items: center;
transform: translateY(-80px);
@add-mixin transition-cubic;
transition-delay: 1.2s;
.logo {
margin-left: 0;
img {
height: 48px;
}
}
.phone-email {
margin-right: 0;
margin-left: auto;
margin-top: 5px;
text-align: right;
color: #fff;
img {
float: right;
height: 34px;
margin-left: 15px;
}
h4 {
margin-bottom: -5px;
margin-top: 3px;
font-size: 17px;
font-weight: 600;
line-height: 1;
}
small {
line-height: 1;
a {
opacity: 0.5;
color: #fff;
&:hover {
text-decoration: none;
color: $color-light;
opacity: 1;
}
}
}
}
.language {
margin-right: 40px;
margin-left: 60px;
padding: 10px 0;
padding-right: 30px;
border-right: 1px solid rgba(255, 255, 255, 0.15);
a {
display: inline-block;
margin: 0 5px;
color: #fff;
font-weight: 600;
&:hover {
color: $color-light;
text-decoration: none;
}
}
}
.hamburger {
width: 30px;
height: 21px;
margin-right: 0;
}
}
.menu {
width: 100%;
display: flex;
flex-wrap: wrap;
border-top: 1px solid rgba(255, 255, 255, 0.15);
@add-mixin transition-cubic;
transition-delay: 1.4s;
opacity: 0;
ul {
display: flex;
flex-wrap: wrap;
margin-left: auto;
margin-right: 0;
margin-top: -1px;
li {
margin: 0;
margin-left: 40px;
padding: 0;
list-style: none;
position: relative;
&:hover ul {
top: 100%;
opacity: 1;
visibility: visible;
}
ul {
min-width: 220px;
position: absolute;
left: -35px;
top: 120%;
background: $color-dark;
margin: 0;
padding: 25px 0;
opacity: 0;
visibility: hidden;
@add-mixin transition;
&:before {
content: "";
width: 0;
height: 0;
border-style: solid;
border-width: 0 10px 10px 10px;
border-color: transparent transparent $color-dark
transparent;
position: absolute;
left: 35px;
top: -10px;
}
li {
margin: 0;
padding: 0 35px;
white-space: nowrap;
a {
padding: 8px 0;
&:hover {
border-color: transparent;
}
}
}
}
a {
color: #fff;
display: inline-block;
font-weight: 600;
padding: 25px 0;
border-top: 1px solid transparent;
&:hover {
text-decoration: none;
color: $color-light;
border-top: 1px solid $color-light;
}
}
}
}
}
}
}
</style>
ERROR
error postcss-mixins: /mnt/site/src/components/Navbar.astro?astro&type=style&lang.css:22:7: Undefined mixin transition-cubic
at /mnt/site/src/components/Navbar.astro?astro&type=style&lang.css:22:7
at Input.error (/mnt/site/node_modules/postcss/lib/input.js:148:16)
at Proxy.error (/mnt/site/node_modules/postcss/lib/node.js:60:32)
at insertMixin (/mnt/site/node_modules/postcss-mixins/index.js:126:18)
at add-mixin (/mnt/site/node_modules/postcss-mixins/index.js:211:13)
at LazyResult.visitTick (/mnt/site/node_modules/postcss/lib/lazy-result.js:502:16)
at LazyResult.runAsync (/mnt/site/node_modules/postcss/lib/lazy-result.js:410:30)
at async compileCSS (/mnt/site/node_modules/vite/dist/node/chunks/dep-59dc6e00.js:35762:27)
at async transform (/mnt/site/node_modules/vite/dist/node/chunks/dep-59dc6e00.js:35313:55)
at async preprocessStyle (file:///mnt/site/node_modules/astro/dist/vite-plugin-astro/compile.js:44:24)l
So the src/styles/mixins.css has already been processed out (from what I'm assuming) by the time scoped style hits?
Sorry for dropping the ball on this, @ran-dall! Really appreciate the helpful reproduction.
So the src/styles/mixins.css has already been processed out (from what I'm assuming) by the time scoped style hits?
That might be the case, but you're correct that this should be working. I'll get this into our backlog and hopefully we can figure out what is going on here soon.
@natemoo-re 🤝 I appreciate you picking it back up.
@ran-dall I'm digging into this one now to see where the page-level postcss mixins are being lost in the component, in the meantime I noticed there's a bit of a work around in case this is blocking you on anything
<style>
@import "/src/styles/mixins.css";
.navbar {
...
}
</style>
Not ideal as the ultimate fix, but importing the mixins to the component directly will at least make sure they're defined when scoping styles. Hoping to find the right fix to handle this as expected though :+1:
@tony-sull Thanks brother for looking into this. Yeah, this is what I've been doing to work around the problem but it becomes exceedingly harder when you start having to do this once for variables, mixins, etc...but I agree, at least it unblocks the issue.
It turns out the fix for this will be pretty complex and a bit risky as we're prepping for 1.0. Bumping this down to a p2-has-workaround but this will be a great one for us to dig into post-1.0 to see what PostCSS improvements we can make!
It turns out the fix for this will be pretty complex and a bit risky as we're prepping for 1.0. Bumping this down to a
p2-has-workaroundbut this will be a great one for us to dig into post-1.0 to see what PostCSS improvements we can make!
Could you please keep this tagged as important? Working smoothly with PostCSS is pretty critical - how easy it is to fix is separate from how important the bug is!
@tony-sull Thank you for looking into it, mate. Appreciate it. It makes sense that it would be a post-1.0 fix.
@pikeas Understand your concern; however, we do have a workable workaround for the time being, and I think the Team's focus right now is 1.0. If you need help making that work, feel free to pay us a visit at Support Squad on Discord.