-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Add functional utility syntax #15455
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
bf7dc05
to
13e75f4
Compare
In Resolving against |
it was a typo — fixed. |
c91ccb7
to
4eba187
Compare
cfe0f82
to
99b89e4
Compare
packages/tailwindcss/src/index.ts
Outdated
import { compoundsForSelectors } from './variants' | ||
export type Config = UserConfig | ||
|
||
const IS_VALID_PREFIX = /^[a-z]+$/ | ||
const IS_VALID_UTILITY_NAME = /^[a-z][a-zA-Z0-9/%._-]*$/ | ||
const IS_VALID_FUNCTIONAL_UTILITY_NAME = /^-?[a-z][a-zA-Z0-9/%._-]*-\*$/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oxide currently ignores capitals in any class name. Both of these regexes should probably account for that? Or we should relax that restriction in Oxide (but iirc it resulted in a pretty decent reduction in potential candidates).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, what do you think, just drop the A-Z
part?
Complex part is if you have tab-*
but you use tab-myTabSize
, should we validate that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So after looking into this, what we disallow right now (in Oxide) is camelCasing if it's the first segment of the candidate. So fooBar
would not work. foo-Bar
would work, hover:fooBar
would also work.
This is a bit confusing in general, but feels like outside of the scope of this PR to fix. However, @thecrypticace and I talked about this, and we believe the proper way to solve this, is to provide the root
s of the static
and functional
utilities and the variants to Oxide such that we have a much smaller and more correct list to work with. There could of course still be false positives (e.g.: text-md
that looks okay, but might not exist). In that case we could provide the full theme as well, but that seems overkill.
Will explore this after the v4 launch, probably.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example, from the tests:
@utility example-* {
--value-as-number: value(number);
--value-as-percentage: value(percentage);
--value-as-ratio: value(ratio);
}
produces this for example-2/3
:
.example-2\\/3 {
--value-as-number: 2;
--value-as-ratio: 2 / 3;
}
which feels incorrect. I'm pretty sure it should be:
.example-2\\/3 {
--value-as-ratio: 2 / 3;
}
Especially since this:
@utility example-* {
--value-as-number: value(number);
}
prints no CSS at all for example-2/3
.
Also, we're going to want to change |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome stuff! @thecrypticace brought up some good points
Yeah that's true and confusing. The idea is that if you have |
- If you are using a bare value or modifier that is a number, then we make sure that it is a valid multiplier of `0.25` - If you are using a bare value or modifier that is a percentage, then we make sure that it is a valid positive integer. - If you are using a fraction, then we make sure that both the numerator and denominator are positive integers. - If the bare value resolves to a non-ratio value, and if a modifier is used, then we need to make sure that the modifier resolves as well. E.g.: `example-1/2.3` this won't resolve to a `ratio` because the denominator is invalid. This will resolve to an `integer` or `number` for the value of `1`, but then we need to make sure that `2.3` is a valid modifier.
99b89e4
to
4d2c36b
Compare
This makes it prettier / biome friendly.
f58c659
to
392dce9
Compare
1aadda6
to
04705ab
Compare
Because of `@utility -foo-*`, this means that we can (and should) drop the `supportsNegative` from the suggestions API, otherwise it would suggestion `--foo-123`.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking really good already! Left a question inline about the ratio
branches, cause I wonder if we can simplify this a bit if we can avoid resolving --value(integer)
at all when a ratio is used. Not sure if we can, though
// - If a candidate looks like `foo-2/3`, then the `--value(ratio)` should | ||
// be used OR the `--value(…)` and `--modifier(…)` must be used. But not | ||
// both. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there any valid use case for resolving a ratio like foo-2/3
as --value(…)
and --modifier(…)
? If not I wonder if this might simplify things a bit since we can just determine in advance wether the input looks like a ratio
and never resolve the --value(number)
in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sadly there is, if you define your font-sizes as numbers instead of tshirt sizes. E.g.: text-4/7
could be text-4/7
(font-size of 4/7
whatever that means) or text-4
with line-height of 7
.
Could we pipe this to @apply directive ? Using new variables: @theme {
--tab-size-1: 1;
--tab-size-2: 2;
--tab-size-4: 4;
--tab-size-github: 8;
}
@utility tab-* {
/* tab-1, tab-2, tab-4, tab-github */
tab-size: --value(--tab-size-*);
}
@utility foo {
@apply tab-1
} Using existing variables: @theme {
// ...
--color-amber-500: #fff;
// ...
}
@utility card-* {
/* card-sky-100 */
background-color: --value(--color-*);
}
@utility bar {
@apply card-sky-100
} |
I would also like an answer to this, I would assume any tailwind utility is able to be used in |
This PR adds support for functional utilities constructed via CSS.
Registering functional utilities in CSS
To register a functional utility in CSS, use the
@utility potato-*
syntax, where the-*
signals that this is a functional utility:Resolving values
The special
--value(…)
function is used to resolve the utility value.Resolving against
@theme
valuesTo resolve the value against a set of theme keys, use
--value(--theme-key-*)
:Bare values
To resolve the value as a bare value, use
--value({type})
, where{type}
is the data type you want to validate the bare value as:Arbitrary values
To support arbitrary values, use
--value([{type}])
(notice the square brackets) to tell Tailwind which types are supported as an arbitrary value:Supporting theme values, bare values, and arbitrary values together
All three forms of the
--value(…)
function can be used within a rule as multiple declarations, and any declarations that fail to resolve will be omitted in the output:This makes it possible to treat the value differently in each case if necessary, for example translating a bare integer to a percentage:
The
--value(…)
function can also take multiple arguments and resolve them left to right if you don't need to treat the return value differently in different cases:Negative values
To support negative values, register separate positive and negative utilities into separate declarations:
Modifiers
Modifiers are handled using the
--modifier(…)
function which works exactly like the--value(…)
function but operates on a modifier if present:If a modifier isn't present, any declaration depending on a modifier is just not included in the output.
Fractions
To handle fractions, we rely on the CSS
ratio
data type. If this is used with--value(…)
, it's a signal to Tailwind to treat the value + modifier as a single value: