Closed lukasluecke closed 4 years ago
Hey Lukas Twin only converts Tailwind classes to object styles and plugins aren't supported... yet. It's a feature I really want to add and it's on the TODO list. Cheers
Yeah that's what I figured as well, no worries. If there's anything I can do to help let me know ๐
I would also vote for this feature, cause this seems to be the thing preventing me to migrate from tailwind.macro to twin
@vladferix Twin stemmed from tailwind.macro so there shouldn't be a difference in the way it handles plugins. If you've tailwind.macro loads the plugin correctly (and doesn't in twin) could you please let me know the plugin so I can look into it further?
@ben-rogerson in my tailwind.config I write something like this
plugins: [
require("tailwindcss-transitions")(),
require("tailwindcss-transforms")(),
function({ addUtilities }) {
const newUtilities = {
".type-sm": {
fontSize: fontSize.sm,
fontWeight: fontWeight.medium,
lineHeight: lineHeight.tight
}
}
addUtilities(newUtilities);
}
]
I got the error:
โ โtype-smโ was not found in the Tailwind config.
Learn more: https://www.npmjs.com/package/twin.macro
at /node_modules/twin.macro/macro.js:2194:13
at Array.reduce (<anonymous>)
at getStyles (/node_modules/twin.macro/macro.js:2170:33)
at /node_modules/twin.macro/macro.js:2349:38
at Array.forEach (<anonymous>)
at twinMacro (/node_modules/twin.macro/macro.js:2339:22)
at macroWrapper (/node_modules/babel-plugin-macros/dist/index.js:63:12)
at applyMacros (/node_modules/babel-plugin-macros/dist/index.js:230:14)
at ImportDeclaration (/node_modules/babel-plugin-macros/dist/index.js:114:28)
at NodePath._call (/node_modules/@babel/core/node_modules/@babel/traverse/lib/path/context.js:55:20)
The external plugins are complaining in the same fashion In tailwind.macro the following structure is fully working
that's interesting, let me do some digging and I'll get back to you
@ben-rogerson - just wanted to point out this would be useful in allow folks to utilize TailwindUI specific styles too!
https://www.notion.so/Tailwind-UI-Documentation-f9083ed0e2694690ac89253e88afb2b6
@tsaiDavid Expect a Twin release within the next few days - I'm aiming to have plugin support in there ๐
Okay guys, we now have support for custom utilities in your plugin section in twin.macro@1.0.0-alpha.5
.
Full support for Tailwind plugins will take a lot more time to get working.
Read the release notes
Hej @ben-rogerson I should have read that those last messages earlier.
After solving this tiny number bug in #8 I was a bit hooked and discovered that twin.macro
does not use tailwindcss
to actually do it's job.
I think that's fine in the beginning, but parity with the tool it suggest to use would be nice.
So I had a bit of time during the weekeend and spiked potential solutions. The first attempt was rather easy, but involved copying quite a bit from tailwind, which is not desirable from a feature parity point of view.
So for now I came up with two potential solutions, and I saw you using one of them partially by now.
Both of them have in common to use the PostCSS AST, which is the right format for us to get all the knowledge we might need.
Tailwind itself proposes itself to be a PostCSS plugin and while this macro doesn't neceessarily need to support other postcss-*
plugins, we should use what Tailwind plugins use under the hood and is consumed by: postcss
Basically we just create a small document just inserting taiwind.
const template = postcssJs.parse({
'@tailwind base': true,
'@tailwind components': true,
'@tailwind utilities': true,
// potentiall @tailwind screens, but it auto-injected by tailwind
})
Then use processTailwindFeatures
on this.
This basically provides with a full, denormalized, exhaustive set of CSS, still in its reified AST form.
This plugin could built a lookup map like here or just simple rule walkers to find the rules we're interested in.
By walking the parent
, or even using another (potentially selfmade?) plugin we could then extract media queries (I'm really shit when it comes to css, are there any other @rules
we need to account for?) and if the rule is nested in one generate the JS accordingly.
This solution focusses more on Tailwind and might even yield a few PRs for Tailwind to export / refactor things if needed.
It still relies heavily on the tailwindcss
package but pickes only the good parts we want.
We would start with having the processing the plugins like you already did.
(Here we could optimize in the future by having Tailwinds plugins actually lazy rendering, or exposing components definitions better. But nothing we need to care about right now)
Now that we have everything in the object as needed there is just something to understand from a Tailwind perspective:
utilities
are wrapped in variant atRules. Components don't get auto-generated variants but may be wrapped and configured.
Overall you can think about it like displayed here, just in CSS/AST:
plugin(function({ addComponents }) {
addComponents({
'@variants responsive, hover': {
'.btn': {
padding: '.5rem 1rem !important',
borderRadius: '.25rem !important',
fontWeight: '600 !important',
},
// ...
}
})
})
This makes it really convenient to stop here in the "normalized" set of rules and further work on it.
(We might want to replace theme()
calls in values).
This is done once at startup of the macro and would ideally persist (cache somehow or can we rely on Node runtime?)
The resulting AST as CSS looks like this:
@variants responsive {
.tw-italic {
font-style: italic;
}
.tw-not-italic {
font-style: normal;
}
}
@variants responsive, hover, focus {
.tw-text-transparent {
color: transparent;
}
.tw-text-black {
color: #000;
}
.tw-text-white {
color: #fff;
}
.tw-text-gray-100 {
color: #f7fafc;
}
/* ... */
.tw-text-red-100 {
color: #fff5f5;
}
/* ... */
}
Now to the macro "runtime" does the following:
When resolving a set of classes to apply like sm:bg-green md:bg-yellow xl:bg-red sm:hover:text-light
we can resolve this to it'a variants.
responsive
is special in the @variants
and will resolve to the config.screens
keys as accepted prefix values.
e.g.:
`sm:hover:text-light`
// => { utility: 'text-light', variants: ['sm', 'hover'] }
Bby looking on the @variant
atRules in the AST for the utility we can find out whether all of the class names resolve to allowed versions (we can even expand specificity by ignoring the order of variant params, except for responsive. By design it should always be outer-most.)
(We can then use the information from the variant generator functions if needed to build an optimized subset of CSS by using the utility
value as the only rule input to wrap. This may be postponed to when it's needed.)
What we end upt with are CSS rules & declarations we can then expose via postcss-js
as the output of the macro.
postcess
we might get some problems, as e.g. adding other postcss plugins at plugin runtime wouldn't be executed. Not sure this is an issue.After one of those is done, we have all the ulities the tailwind.config.js
(plus default config) defined.
Yes, this means you don't need the duplication within ./config
anymore! :tada:)
This also means deactivating not used utilites in corePlugins
will work as expected, didn't research whether that's currently the case with this macro.
But in the latest state of knowledge it looks like you generate the processed plugins over and over again, containing the defaults all the time, and never make use that default rules are plugin based. Every change to these has to be manually reflected by you in your config folder and maybe even code, right? (No complain here, just trying to remember all of my selling points :laughing: )
Oh, and btw I tested this with custom-forms
over the weekend to make sure external plugins are working. Didn't yell and saw properly prefixed, and properly non-varianted, form classes in the output.
And just one more thing, it would need its own ticket: having the AST it's a breeze to get us TypeScript typings (which even helps vanilla JS users when they're for example in VS Code).
@zcei Sounds awesome! Thanks for your work on this ๐
I had some of the same ideas regarding actually "running" tailwindcss
but no time to work on it. Out of your proposed solutions I would be in favor of the 2nd one, if there is no issue with any of the most popular plugins (i.e. your 2nd "disadvantage" doesn't affect them).
@lukasluecke I just looked on NPM and there seem to be quite some plugins. Do you have a list, or would be willing to create one, that I can turn into e2e dependencies? I have no clue which ones are really important to the ecosystem.
For context, I'm really not a design or CSS experienced person at all. This is why I like the rise of style systems so much: I don't need to know all of this as long as I can express simple intents of "this should look like something darkish blue and should be three items per row" ๐
My area of interest is more on backend and language transform side of thing, so I thought I could be of help here. I just need proper input - usually that's @axe312ger job, feel free to chime in ๐
Hey zcei, thanks for looking into this issue further.
I'd prefer the second option more also and I'm really interested in the idea of removing the need for the ./config
files and letting Twin rely more upon tailwindcss for parsing the tailwind config.
You mentioned you tried the idea with custom-forms
- would you have any code to take a look at?
@ben-rogerson it's the infamous weekend coding night messy state, but I think I could get a rough versions together towards the weekend.
Scope would be using tailwindcss
core plugins instead of the config (but without inlined media queries for now, just as a stretch goal) and custom-forms
as en example of a community plugin.
Those two things are basically solved together in there and kinda go hand in hand, so I think it would take more time separating this from each other.
Great work on getting 'addUtilities' live in Alpha 5 :)
Is there an eta for 'addComponents'?
Yes, now it is working pretty awesome in our project, looking forward to further support!
Is there an eta for 'addComponents'?
Sorry no eta yet - I'll keep you posted
Since plugin support is still WIP, could you update the docs on what is supported and what not? Maybe also add a list of "to do" items including links to GH issues.
All variants, all the time
Looks a bit misleading to me, since addVariants
is not supported yet ;) At least, that is what I found out when I tried to use the dark-mode plugin
Yeah, I'll add some more details shortly about the current plugin support. For the list of todos, take a look at GitHub Projects.
So I've been making progress on plugin support, shouldn't be too long now. Here's the summary:
[x] addUtilities
- for registering new utility styles
Currently supported
[x] addComponents
- for registering new component styles
Adding in the next release
addVariant
- for registering custom variants
This can't be supported right now
addBase
- for registering new base stylesWe don't require support for this at the moment Support was later added
v1.4.0 is up with support for addComponents
๐
I've also started listing plugins and showing twin support.
I'll close this topic for now as addVariant
isn't going to be supported anytime soon.
@ben-rogerson What's the technical reason for not being able to support addVariant
?
Also I know twin adds in many more bartenura, but what should the workaround be if we'd like to use some new custom variants to use with twin?
@ben-rogerson What's the technical reason for not being able to support
addVariant
?
It's been a while since I looked at this, but I remember there being issues due to the differences in tailwind who generates all the styles ahead of time vs twin that generates the css after you use a class. I think it's probably worth another look on my part to see if I can come up with another solution.
The only workaround at the moment would be to generate the classes with the variants in a tailwind plugin or use a javascript function that adds the variant(s) with vanilla css instead. If you'd like some help with this, perhaps it's best to open a new discussion including some examples of what you'd like to do here.
Just a quick note to anyone interested here - I've opened up a discussion about adding addVariant
support.
I tried to use https://tailwindcss-custom-forms.netlify.com but seems like it can't find the new classes.