Open mrtamagotchi opened 4 months ago
After a lot of thinking and som AFK discussions with other devs I've come to a couple of conclusions:
... and you should probably simplify your component if you need it. This was a clear case of "Your scientists were so preoccupied with whether or not they could, they didn't stop to think if they should" - Jeff Goldblum, Jurassic Park, 1995.
Twiv should be simple and cover most common cases. Thinking back on all my years as a dev, the most common cases of styling on multiple states has been either stateA && stateB
or stateA || stateB
. Once again, if more complex styling is needed, Twiv should not be the solution.
Tying into the point above, stateA && stateB && stateC
becomes redundant in two ways:
eval()
(which could pose security risk), or by building a lexer (which is more secure but probably less performant). Either way, both of these options are too taxing to be running in multiple className
s per component. Given the findings above I've merged in a solution I decided to call Opcodes
. The syntax follows the pattern ALL: <list of variant state names separated by a space>
. I've chosen to have it in caps as it (somewhat creatively) follows the Tailwind config syntax of "DEFAULT:" (if you squint and pretend that DEFAULT:
is considered to be some kind of "meta-value").
Currently, two opcodes exist:
ALL:
that applies styles if all states matchANY:
that applies styles if any state matchesThis removes the need for typing out multiple operators, makes evaluation a lot more performant and gives you a pretty sweet and concise syntax. It's also easily extandable if the need for other evaluations would arise.
With this, the example in the first post in this thread, it would look like this:
twiv({
small: "text-sm",
large: "text-lg",
left: "text-left",
right: "text-right",
"ALL: left small": "pl-1",
"ALL: left large": "pl-4",
"ALL: right small": "pr-1",
"ALL: right large": "pl-4"
})
The biggest downside of this solution is that TS support becomes a bit shady. Ideally, you would want a type that checks for a string that follows the pattern: One TwivOpcode
, n
amount of ValidVariantName
, all separated by spaces. This, unfortunately, makes TS shit its pants for components with a reasonable amount of states as such a type quickly outgrows the max of 100_000 variations.
I've decided to go for this solution for now, just to see how it feels in a real project. The Opcodes
feature is marked as experimental
, until there is a final verdict of its ergonomics, performance and usefulness.
Background
Currently, Twiv can only apply classes matching one state. If we would like to add a class if
propA === X && propB === Y
we have to do some JS gymnastics.Possible solution
Only half-baked ideas here, but some operator syntax could be interesting:
A question of operators
If this is a good feature to add, there are three main questions that need to be addressed:
Syntax: What should the syntax look like? I think that a string with an operator is the cleanest way to go, but I could be wrong.
Support: The operators will most likely need to be parsed from the string and handled manually in the lib. What is the minimum amount of logical operations that need to be supported? My assumption is
n
amount oflogical AND
andn
amount oflogical OR
. Maaaybe ann
amount oflogical NAND
, but that seems like an edge case.Operator style: How should the operators be written? I would like to carry over the default logical operators in JS. There is however a case to be made that using
&
and|
is a bit cleaner looking (esp if NAND becomes a thing and we need a custom!&
/!&&
one). They could be confused with JS bitwise operators though, which is slightly nasty.Uncertainties