Repository: tailwindlabs/tailwindcss

Problem statement:
[JIT] font/text size does not support css variables
### What version of Tailwind CSS are you using?

2.1.0

### What build tool (or framework if it abstracts the build tool) are you using?

postcss8

### What version of Node.js are you using?

15.14.0

### What browser are you using?

Firefox

### What operating system are you using?

Arch Linux

### Reproduction repository

See comment

### Describe your issue

Hey guys!
I try to implement fluid font sizes according to https://www.smashingmagazine.com/2021/04/designing-developing-fluid-type-space-scales/

I have [generated the following pattern](https://utopia.fyi/type/calculator):
```css
/* @link https://utopia.fyi/type/calculator?c=320,21,1.2,1140,24,1.25,5,2,&s=0.75|0.5|0.25,1.5|2|3|4|6,s-l */

:root {
  --step--2: clamp(0.91rem, 0.89rem + 0.10vw, 0.96rem);
  --step--1: clamp(1.09rem, 1.05rem + 0.21vw, 1.20rem);
  --step-0: clamp(1.31rem, 1.24rem + 0.37vw, 1.50rem);
  --step-1: clamp(1.58rem, 1.46rem + 0.59vw, 1.88rem);
  --step-2: clamp(1.89rem, 1.71rem + 0.89vw, 2.34rem);
  --step-3: clamp(2.27rem, 2.01rem + 1.29vw, 2.93rem);
  --step-4: clamp(2.72rem, 2.36rem + 1.83vw, 3.66rem);
  --step-5: clamp(3.27rem, 2.75rem + 2.56vw, 4.58rem);
}
```

But the syntax `text-[var(--step-5)]` does not work. Using a fixed pixel size works, and using the style inline also works.

```html
<div class="text-[var(--step-5)]">test1</div>

<div class="text-[50px]">test2</div>

<div style="font-size: var(--step-5);">test3</div>
```


Hints:
Yeah this is tough because of naming conflicts. Tailwind has classes like `text-sm` but also `text-black`, so when you say `text-[var(--foo)]` we don't know if that is a color or a font-size.

I think one way we could solve this is having a dedicated plugin for handling arbitrary text values that generates this:

```css
.text-\[var\(--foo\)\] {
  font-size: var(--foo);
  color: var(--foo);
}
```

...and making sure those styles are inserted before regular font size and text color utilities, so you could still override them.

Will leave this open until have a chance to explore that and see if it's a viable solution.
As an alternative, adding fluid text sizes would also work for me. That does not address the fact, that variables will still not work, but will fix my root cause. Did you consider adding fluid designs to tailwind yet? I really like the idea, but you have more experience in css than me:
https://www.smashingmagazine.com/2021/04/designing-developing-fluid-type-space-scales/

Patch:
diff --git a/src/jit/lib/generateRules.js b/src/jit/lib/generateRules.js
--- a/src/jit/lib/generateRules.js
+++ b/src/jit/lib/generateRules.js
@@ -192,9 +192,13 @@ function* resolveMatchedPlugins(classCandidate, context) {
   }
 }
 
+function splitWithSeparator(input, separator) {
+  return input.split(new RegExp(`\\${separator}(?![^[]*\\])`, 'g'))
+}
+
 function* resolveMatches(candidate, context) {
   let separator = context.tailwindConfig.separator
-  let [classCandidate, ...variants] = candidate.split(separator).reverse()
+  let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
   let important = false
 
   if (classCandidate.startsWith('!')) {
diff --git a/src/jit/lib/setupContext.js b/src/jit/lib/setupContext.js
--- a/src/jit/lib/setupContext.js
+++ b/src/jit/lib/setupContext.js
@@ -534,9 +534,9 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
 
         function wrapped(modifier) {
           let { type = 'any' } = options
-          let value = coerceValue(type, modifier, options.values)
+          let [value, coercedType] = coerceValue(type, modifier, options.values)
 
-          if (value === undefined) {
+          if (type !== coercedType || value === undefined) {
             return []
           }
 
diff --git a/src/util/pluginUtils.js b/src/util/pluginUtils.js
--- a/src/util/pluginUtils.js
+++ b/src/util/pluginUtils.js
@@ -204,6 +204,18 @@ let typeMap = {
   lookup: asLookupValue,
 }
 
+function splitAtFirst(input, delim) {
+  return (([first, ...rest]) => [first, rest.join(delim)])(input.split(delim))
+}
+
 export function coerceValue(type, modifier, values) {
-  return typeMap[type](modifier, values)
+  if (modifier.startsWith('[') && modifier.endsWith(']')) {
+    let [explicitType, value] = splitAtFirst(modifier.slice(1, -1), ':')
+
+    if (value.length > 0 && Object.keys(typeMap).includes(explicitType)) {
+      return [asValue(`[${value}]`, values), explicitType]
+    }
+  }
+
+  return [typeMap[type](modifier, values), type]
 }


Test patch:
diff --git a/tests/jit/arbitrary-values.test.css b/tests/jit/arbitrary-values.test.css
--- a/tests/jit/arbitrary-values.test.css
+++ b/tests/jit/arbitrary-values.test.css
@@ -269,12 +269,18 @@
 .text-\[2\.23rem\] {
   font-size: 2.23rem;
 }
+.text-\[length\:var\(--font-size\)\] {
+  font-size: var(--font-size);
+}
 .leading-\[var\(--leading\)\] {
   line-height: var(--leading);
 }
 .tracking-\[var\(--tracking\)\] {
   letter-spacing: var(--tracking);
 }
+.text-\[color\:var\(--color\)\] {
+  color: var(--color);
+}
 .placeholder-\[var\(--placeholder\)\]::placeholder {
   color: var(--placeholder);
 }
diff --git a/tests/jit/arbitrary-values.test.html b/tests/jit/arbitrary-values.test.html
--- a/tests/jit/arbitrary-values.test.html
+++ b/tests/jit/arbitrary-values.test.html
@@ -54,6 +54,9 @@
     <div class="skew-x-[3px]"></div>
     <div class="skew-y-[3px]"></div>
     <div class="text-[2.23rem]"></div>
+    <div class="text-[length:var(--font-size)]"></div>
+    <div class="text-[color:var(--color)]"></div>
+    <div class="text-[angle:var(--angle)]"></div>
     <div class="duration-[2s]"></div>
     <div class="m-[7px]"></div>
     <div class="mx-[7px]"></div>