Open andreich1980 opened 4 years ago
I think the best way would be to actually sort thanks to the configuration.
It could, for instance, add a VSCode configuration item headwind.groupBy
that would take an array of Tailwind CSS configuration keys (eg. [ "spacing", "colors" ]
) and use that to order. That way, custom classes would also be taken into account.
For a more precise sorting, a configuration item headwind.sort
could be used, with an array of class names.
Another last thing, it could be interesting to add a class separator. Willem Van Bockstal uses |
and I find that pretty interesting.
{
"headwind.groupBy": [
"container",
"spacing",
"fontFamily",
"colors",
],
"headwind.sort": [
"my-class", // can be classes that have nothing to do with Tailwind
],
"headwind.separator": "|"
}
1) When use "spacing"
how do I choose what goes first m-N
or p-N
?
2) How would you use the |
? To separate what classes/group of classes? mx-2 my-3 | px-4 py-2 | flex
?
I replaced the example config:
gorupBy
item is an object with namespaces as keys, and an array of regex as values,headwind.customFirst
option would determine if non-Tailwind classes would be placed first or lastheadwind.separator
can be an object with a character
key instead, and a threshold
key that would indicate the required amount of characters in the class attribute for the separator to be actually used (to avoid bloating HTML).{
"headwind.groupBy": {
"container": [],
"spacing": ["\\-", "m", "p"], // would order negative classes first, then margin, then padding for the spacing namespace
"fontFamily": [],
"colors": ["text", "bg", "border"] // would place text color utilities before background, and border last
},
"headwind.customFirst": true, // places custom (non-tailwind) classes first if true, last if false
"headwind.sort": [
// can be classes that have nothing to do with Tailwind
"my-class",
"another-class"
],
"headwind.separator": {
"character": "|",
"threshold": 20 // add the separator only if more than 20 classes?
}
}
I like this approach! Great idea 💡
And if separator character is an empty string - don't use it.
And the "headwind.customFirst": true
option is what I was thinking about but haven't mentioned it yet!
If there is no PR in the next couple of days, I might want to implement this myself, if @heybourn likes this pattern. Or maybe I can do something in a separated package, since someone on Twitter has mentioned that a CLI tool for this would be appreciated. 😊
I'd love if you made a PR. I agree, I think slimming the code down to prefixes would be a better approach
So, I'm going to make a separate package for this. Its API will take the classlist and parameters as arguments, and will return the sorted classlist. That way, it can be reusable accross multiple extensions and still be consistent, and it can also be used as a CLI tool.
Well, looks like this won't be possible without Tailwind giving a map of configuration ›› class names.
Currently, there is no way to know which class will be generated from which plugin (core or not). It means we can't actually sort the class names thanks to this kind of configuration.
What's required is a map like that:
{
"height": [
".h-0",
".h-1",
".h-2",
".h-3",
".h-4",
".h-5",
".h-6",
".h-8",
".h-10",
".h-12",
".h-16",
".h-20",
".h-24",
".h-32",
".h-40",
".h-48",
".h-56",
".h-64",
".h-auto",
".h-px",
".h-full",
".h-screen",
],
"lineHeight": [
".leading-none",
".leading-tight",
".leading-snug",
".leading-normal",
".leading-relaxed",
".leading-loose",
]
}
I guess I'm giving up that idea for now, until it gets implemented in Tailwind 😦
Should we ping @tailwindcss to ask for Adam's opinion?
@andreich1980 He said on the Discord server that he was open to PRs. Feel free to come in order to ask before doing any work, though.
Managed to get core to give me something like this. Would this be enough? What else are we missing?
Here's what the new version of the file could look like.
// .src/processTailwindFeatures.js
import _ from 'lodash'
import postcss from 'postcss'
import fs from 'fs'
import substituteTailwindAtRules from './lib/substituteTailwindAtRules'
import evaluateTailwindFunctions from './lib/evaluateTailwindFunctions'
import substituteVariantsAtRules from './lib/substituteVariantsAtRules'
import substituteResponsiveAtRules from './lib/substituteResponsiveAtRules'
import substituteScreenAtRules from './lib/substituteScreenAtRules'
import substituteClassApplyAtRules from './lib/substituteClassApplyAtRules'
import corePlugins from './corePlugins'
import processPlugins from './util/processPlugins'
export default function(getConfig) {
return function(css) {
const config = getConfig()
const processedPlugins = processPlugins([...corePlugins(config), ...config.plugins], config)
const classes = Object.entries(processedPlugins)
.map(([feature, postcssThings]) => {
if (feature === 'base') return []
return (_.isArray(postcssThings) ? postcssThings : []).map(thing => {
return thing.nodes.map(node => node.selector)
})
})
.filter(array => array.length) // Filters empty arrays from `base` and `variantGenerators`
fs.writeFile(`tailwind.classes.map.json`, JSON.stringify({ classes }, null, 2), () => {})
return postcss([
substituteTailwindAtRules(config, processedPlugins),
evaluateTailwindFunctions(config),
substituteVariantsAtRules(config, processedPlugins),
substituteResponsiveAtRules(config),
substituteScreenAtRules(config),
substituteClassApplyAtRules(config, processedPlugins.utilities),
]).process(css, { from: _.get(css, 'source.input.file') })
}
}
I was also looking into how to group those classes by plugin. It seems like this is going to be a little harder to solve because even if core plugins are named (name is not accessible right now but it's just a matter of passing it around until you save it on the produced postcss AST), third-party plugins are not.
We could probably solve this partially if plugin authors add a name
key to the plugin object syntax that will be introduced with v1.2.0.
This is a really good start, but unfortunately plugin names are required for the headwind.groupBy
configuration idea. :(
But with that list, it sounds like a lot more if possible with the plugin (notably the customFirst
and separator
options). Good work 👍
What if we mimic the order from the actual generated css? I think @adamwathan suggested this in discord too.
What if we just used a regex for each item in the sortOrder config? Within each regex for simplicity maybe just sort by string asc?
Might be too complicated and not controllable enough again though.
What do you think about something like that:
"headwind.defaultSortOrder": [
"container",
"m-([0-9]|auto|px)",
"mx-([0-9]|auto|px)",
"ml-([0-9]|auto|px)",
"mr-([0-9]|auto|px)",
"my-([0-9]|auto|px)",
"mt-([0-9]|auto|px)",
"mb-([0-9]|auto|px)",
"-m-([0-9]|auto|px)",
...
"p-([0-9]|auto|px)",
...
]
+1
Hi there! Super plugin!
Is there a way to just sort everything alphabetically? Not sure if that goes against the whole "opinionated" ethos.
Looking for the same @Flerox !
@Flerox @ksample89
you could go to settings UI then search for headwind
then click on Edit in settings.json
then copy the headwind.defaultSortOrder
array which is about 1800 lines, then go to any websites that could sort words alphabetically like https://alphabetizer.flap.tv/ paste and alphabetize then replace the old array with the new sorted one.
Note: You might need to reload the extension.
It would be cool if I could set up preferred sort order not with full class names like
m-0
,m-12
,-m-2
etc but justm
and-m
. This way I wouldn't have to add all my custom tweaks oftailwindcss.config.js
before
after
Though I don't know how to manage classes like
flex
,flex-col
in this case. Maybe there should be 2 config sections: one to manage tags with numbers (likem-N
,p-N
,border-N
, etc. And another section for full class names.Great extension btw!