mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
text-* and leading-* classes (#19396)
This PR improves the canonicalization when using `text-*` and `leading-*` utilities together. When using classes such as: ```html <div class="text-sm leading-7"></div> ``` Then the canonical way of writing this is: ```html <div class="text-sm/7"></div> ``` Similarly, if you already have a modifier applied, and add a new line-height utility. It will also combine them into the canonical form: ```html <div class="text-sm/6 leading-7"></div> ``` becomes: ```html <div class="text-sm/7"></div> ``` This is because the final CSS output of `text-sm/6 leading-7` is: ```css /*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */ .text-sm\/6 { font-size: var(--text-sm, 0.875rem); line-height: calc(var(--spacing, 0.25rem) * 6); } .leading-7 { --tw-leading: calc(var(--spacing, 0.25rem) * 7); line-height: calc(var(--spacing, 0.25rem) * 7); } @property --tw-leading { syntax: "*"; inherits: false; } ``` Where the `line-height` of the `leading-7` class wins over the `line-height` of the `text-sm/6` class. ### Implementation #### On the fly pre-computation Right now, we are not using any AST based transformations yet and instead rely on a pre-computed list. However, with arbitrary values we don't have pre-computed values for `text-sm/123` for example. What we do instead is if we see a utility that sets `line-height` and other utilities set `font-size` then we pre-compute those computations on the fly. We will prefer named font-sizes (such as `sm`, `lg`, etc). We will also prefer bare values for line-height (such as `7`) over arbitrary values (such as `[123px]`). #### Canonicalization of the CSS AST Another thing we had to do is to make sure that when multiple declarations of the same property exist, that we only keep the last one. In the real world, multiple declarations of the same value is typically used for fallback values (e.g.: `background-color: #fff; background-color: oklab(255 255 255 / 1);`). But for our use case, I believe we can safely remove the earlier declarations to make the most modern and thus the last declaration win. #### Trying combinations based on `property` only One small change we had to make is that we try combinations of utilities based on property only instead of property _and_ value. This is important for cases such as `text-sm/6 leading-7`. These 2 classes will set a `lin-height` of `24px` and `28px` respectively so they will never match. However, once combined together, there will be 2 line-height values, and the last one wins. The signature of `text-sm/6 leading-7` becomes: ```css .x { font-size: 14px; /* From text-sm/6 */ line-height: 24px; /* From text-sm/6 */ line-height: 28px; /* From leading-7 */ } ``` ↓↓↓↓↓↓↓↓↓ ```css .x { font-size: 14px; /* From text-sm/6 */ line-height: 28px; /* From leading-7 */ } ``` This now shows that just `text-sm/7` is the canonical form. Because it produces the same final CSS output. ## Test plan 1. All existing tests pass 2. Added a bunch of new tests where we combine `text-*` and `leading-*` utilities with named, bare and arbitrary values. Even with existing modifiers on the text utilities. <img width="1010" height="1099" alt="image" src="https://github.com/user-attachments/assets/d2775692-a442-4604-8371-21dacf16ebfc" />
A utility-first CSS framework for rapidly building custom user interfaces.
Documentation
For full documentation, visit tailwindcss.com.
Community
For help, discussion about best practices, or feature ideas:
Discuss Tailwind CSS on GitHub
Contributing
If you're interested in contributing to Tailwind CSS, please read our contributing docs before submitting a pull request.
Description
Languages
JavaScript
90.6%
CSS
7.6%
HTML
1.7%