2023-01-20 12:45:04 -05:00
|
|
|
import { crosscheck, run, html, css, defaults } from './util/run'
|
|
|
|
|
|
2023-02-17 20:21:22 +01:00
|
|
|
crosscheck(({ stable, oxide }) => {
|
2023-01-20 12:45:04 -05:00
|
|
|
let sharedHtml = html`
|
|
|
|
|
<div class="basic-example"></div>
|
|
|
|
|
<div class="class-order"></div>
|
|
|
|
|
<div class="with-additional-properties"></div>
|
|
|
|
|
<div class="variants"></div>
|
|
|
|
|
<div class="only-variants"></div>
|
|
|
|
|
<div class="apply-group-variant"></div>
|
|
|
|
|
<div class="apply-dark-variant"></div>
|
|
|
|
|
<div class="apply-custom-utility"></div>
|
|
|
|
|
<div class="multiple selectors"></div>
|
|
|
|
|
<div class="multiple-variants selectors-variants"></div>
|
|
|
|
|
<div class="multiple-group selectors-group"></div>
|
|
|
|
|
<div class="complex-utilities"></div>
|
|
|
|
|
<div class="basic-nesting-parent">
|
|
|
|
|
<div class="basic-nesting-child"></div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="use-base-only-a"></div>
|
|
|
|
|
<div class="use-dependant-only-b"></div>
|
|
|
|
|
<div class="btn"></div>
|
|
|
|
|
<div class="btn-blue"></div>
|
|
|
|
|
<div class="recursive-apply-a"></div>
|
|
|
|
|
<div class="recursive-apply-b"></div>
|
|
|
|
|
<div class="recursive-apply-c"></div>
|
|
|
|
|
<div class="use-with-other-properties-base use-with-other-properties-component"></div>
|
|
|
|
|
<div class="add-sibling-properties"></div>
|
|
|
|
|
<div class="important-modifier"></div>
|
|
|
|
|
<div class="important-modifier-variant"></div>
|
|
|
|
|
<div class="a b"></div>
|
|
|
|
|
<div class="foo"></div>
|
|
|
|
|
<div class="bar"></div>
|
|
|
|
|
`
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
test('@apply', () => {
|
|
|
|
|
let config = {
|
2024-01-05 14:39:34 -05:00
|
|
|
darkMode: 'selector',
|
2023-01-20 12:45:04 -05:00
|
|
|
content: [{ raw: sharedHtml }],
|
|
|
|
|
}
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.basic-example {
|
|
|
|
|
@apply rounded-md bg-blue-500 px-4 py-2;
|
|
|
|
|
}
|
|
|
|
|
.class-order {
|
|
|
|
|
@apply p-8 px-3 py-7 pt-4 pr-1;
|
|
|
|
|
}
|
|
|
|
|
.with-additional-properties {
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
@apply text-right;
|
|
|
|
|
}
|
|
|
|
|
.variants {
|
|
|
|
|
@apply font-semibold hover:font-bold focus:font-medium lg:font-light xl:focus:font-black;
|
|
|
|
|
}
|
|
|
|
|
.only-variants {
|
|
|
|
|
@apply hover:font-bold focus:font-medium lg:font-light xl:focus:font-black;
|
|
|
|
|
}
|
|
|
|
|
.apply-group-variant {
|
|
|
|
|
@apply group-hover:text-center lg:group-hover:text-left;
|
|
|
|
|
}
|
|
|
|
|
.apply-dark-variant {
|
|
|
|
|
@apply dark:text-center dark:hover:text-right lg:dark:text-left;
|
|
|
|
|
}
|
|
|
|
|
.apply-custom-utility {
|
|
|
|
|
@apply custom-util hover:custom-util lg:custom-util xl:focus:custom-util;
|
|
|
|
|
}
|
|
|
|
|
.multiple,
|
|
|
|
|
.selectors {
|
|
|
|
|
@apply rounded-md bg-blue-500 px-4 py-2;
|
|
|
|
|
}
|
|
|
|
|
.multiple-variants,
|
|
|
|
|
.selectors-variants {
|
|
|
|
|
@apply hover:text-center active:text-right lg:focus:text-left;
|
|
|
|
|
}
|
|
|
|
|
.multiple-group,
|
|
|
|
|
.selectors-group {
|
|
|
|
|
@apply group-hover:text-center lg:group-hover:text-left;
|
|
|
|
|
}
|
|
|
|
|
.complex-utilities {
|
|
|
|
|
@apply ordinal tabular-nums shadow-lg hover:shadow-xl focus:diagonal-fractions;
|
|
|
|
|
}
|
|
|
|
|
.use-base-only-a {
|
|
|
|
|
@apply font-bold;
|
|
|
|
|
}
|
|
|
|
|
.use-base-only-b {
|
|
|
|
|
@apply use-base-only-a font-normal;
|
|
|
|
|
}
|
|
|
|
|
.use-dependant-only-a {
|
|
|
|
|
@apply font-bold;
|
|
|
|
|
}
|
|
|
|
|
.use-dependant-only-b {
|
|
|
|
|
@apply use-dependant-only-a font-normal;
|
|
|
|
|
}
|
|
|
|
|
.btn {
|
|
|
|
|
@apply rounded py-2 px-4 font-bold;
|
|
|
|
|
}
|
|
|
|
|
.btn-blue {
|
|
|
|
|
@apply btn bg-blue-500 text-white hover:bg-blue-700;
|
|
|
|
|
}
|
|
|
|
|
.recursive-apply-a {
|
|
|
|
|
@apply font-black sm:font-thin;
|
|
|
|
|
}
|
|
|
|
|
.recursive-apply-b {
|
|
|
|
|
@apply recursive-apply-a font-semibold md:font-extralight;
|
|
|
|
|
}
|
|
|
|
|
.recursive-apply-c {
|
|
|
|
|
@apply recursive-apply-b font-bold lg:font-light;
|
|
|
|
|
}
|
|
|
|
|
.use-with-other-properties-base {
|
|
|
|
|
color: green;
|
|
|
|
|
@apply font-bold;
|
|
|
|
|
}
|
|
|
|
|
.use-with-other-properties-component {
|
|
|
|
|
@apply use-with-other-properties-base;
|
|
|
|
|
}
|
|
|
|
|
.add-sibling-properties {
|
|
|
|
|
padding: 2rem;
|
|
|
|
|
@apply px-4 hover:px-2 lg:px-10 xl:focus:px-1;
|
|
|
|
|
padding-top: 3px;
|
|
|
|
|
@apply use-with-other-properties-base;
|
|
|
|
|
}
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
h1 {
|
|
|
|
|
@apply text-2xl sm:text-3xl lg:text-2xl;
|
|
|
|
|
}
|
|
|
|
|
h2 {
|
|
|
|
|
@apply text-2xl;
|
|
|
|
|
@apply lg:text-2xl;
|
|
|
|
|
@apply sm:text-2xl;
|
|
|
|
|
}
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.important-modifier {
|
|
|
|
|
@apply !rounded-md px-4;
|
|
|
|
|
}
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.important-modifier-variant {
|
|
|
|
|
@apply px-4 hover:!rounded-md;
|
|
|
|
|
}
|
2021-09-03 13:48:16 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer utilities {
|
|
|
|
|
.custom-util {
|
|
|
|
|
custom: stuff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.foo {
|
|
|
|
|
@apply animate-spin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bar {
|
|
|
|
|
@apply animate-pulse !important;
|
|
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
2023-02-17 20:21:22 +01:00
|
|
|
stable.expect(result.css).toMatchFormattedCss(css`
|
2023-01-20 12:45:04 -05:00
|
|
|
.basic-example {
|
|
|
|
|
--tw-bg-opacity: 1;
|
|
|
|
|
background-color: rgb(59 130 246 / var(--tw-bg-opacity));
|
2023-01-31 15:37:49 +01:00
|
|
|
border-radius: 0.375rem;
|
|
|
|
|
padding: 0.5rem 1rem;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
.class-order {
|
2023-01-31 15:37:49 +01:00
|
|
|
padding: 1rem 0.25rem 1.75rem 0.75rem;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
.with-additional-properties {
|
|
|
|
|
text-align: right;
|
2023-01-31 15:37:49 +01:00
|
|
|
font-weight: 500;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
.variants {
|
2023-01-20 12:45:04 -05:00
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
.variants:hover {
|
|
|
|
|
font-weight: 700;
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
|
|
|
|
.variants:focus {
|
2023-01-20 12:45:04 -05:00
|
|
|
font-weight: 500;
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.variants {
|
|
|
|
|
font-weight: 300;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1280px) {
|
|
|
|
|
.variants:focus {
|
|
|
|
|
font-weight: 900;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.only-variants:hover {
|
|
|
|
|
font-weight: 700;
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
|
|
|
|
.only-variants:focus {
|
2023-01-20 12:45:04 -05:00
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.only-variants {
|
|
|
|
|
font-weight: 300;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1280px) {
|
|
|
|
|
.only-variants:focus {
|
|
|
|
|
font-weight: 900;
|
|
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
|
|
|
|
.group:hover .apply-group-variant {
|
2023-01-20 12:45:04 -05:00
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.group:hover .apply-group-variant {
|
|
|
|
|
text-align: left;
|
|
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
2024-01-05 14:39:34 -05:00
|
|
|
.apply-dark-variant:where(.dark, .dark *) {
|
2023-01-20 12:45:04 -05:00
|
|
|
text-align: center;
|
|
|
|
|
}
|
2024-01-05 14:39:34 -05:00
|
|
|
.apply-dark-variant:hover:where(.dark, .dark *) {
|
2023-01-20 12:45:04 -05:00
|
|
|
text-align: right;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
2024-01-05 14:39:34 -05:00
|
|
|
.apply-dark-variant:where(.dark, .dark *) {
|
2023-01-20 12:45:04 -05:00
|
|
|
text-align: left;
|
|
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
2023-01-31 15:37:49 +01:00
|
|
|
.apply-custom-utility,
|
2023-01-20 12:45:04 -05:00
|
|
|
.apply-custom-utility:hover {
|
2023-01-19 11:42:52 +01:00
|
|
|
custom: stuff;
|
|
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.apply-custom-utility {
|
|
|
|
|
custom: stuff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1280px) {
|
|
|
|
|
.apply-custom-utility:focus {
|
|
|
|
|
custom: stuff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.multiple,
|
|
|
|
|
.selectors {
|
|
|
|
|
--tw-bg-opacity: 1;
|
|
|
|
|
background-color: rgb(59 130 246 / var(--tw-bg-opacity));
|
2023-01-31 15:37:49 +01:00
|
|
|
border-radius: 0.375rem;
|
|
|
|
|
padding: 0.5rem 1rem;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
.multiple-variants:hover,
|
|
|
|
|
.selectors-variants:hover {
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
.multiple-variants:active,
|
|
|
|
|
.selectors-variants:active {
|
|
|
|
|
text-align: right;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.multiple-variants:focus,
|
|
|
|
|
.selectors-variants:focus {
|
|
|
|
|
text-align: left;
|
|
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
|
|
|
|
.group:hover .multiple-group,
|
|
|
|
|
.group:hover .selectors-group {
|
2023-01-20 12:45:04 -05:00
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.group:hover .multiple-group,
|
|
|
|
|
.group:hover .selectors-group {
|
|
|
|
|
text-align: left;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.complex-utilities {
|
|
|
|
|
--tw-ordinal: ordinal;
|
|
|
|
|
--tw-numeric-spacing: tabular-nums;
|
|
|
|
|
font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure)
|
|
|
|
|
var(--tw-numeric-spacing) var(--tw-numeric-fraction);
|
2023-01-31 15:37:49 +01:00
|
|
|
--tw-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;
|
2023-01-20 12:45:04 -05:00
|
|
|
--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color),
|
|
|
|
|
0 4px 6px -4px var(--tw-shadow-color);
|
|
|
|
|
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
|
|
|
|
var(--tw-shadow);
|
|
|
|
|
}
|
|
|
|
|
.complex-utilities:hover {
|
2023-01-31 15:37:49 +01:00
|
|
|
--tw-shadow: 0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a;
|
2023-01-20 12:45:04 -05:00
|
|
|
--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color),
|
|
|
|
|
0 8px 10px -6px var(--tw-shadow-color);
|
|
|
|
|
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
|
|
|
|
var(--tw-shadow);
|
|
|
|
|
}
|
|
|
|
|
.complex-utilities:focus {
|
|
|
|
|
--tw-numeric-fraction: diagonal-fractions;
|
|
|
|
|
font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure)
|
|
|
|
|
var(--tw-numeric-spacing) var(--tw-numeric-fraction);
|
|
|
|
|
}
|
|
|
|
|
.use-base-only-a {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.use-dependant-only-b {
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
}
|
|
|
|
|
.btn {
|
|
|
|
|
border-radius: 0.25rem;
|
2023-01-31 15:37:49 +01:00
|
|
|
padding: 0.5rem 1rem;
|
2023-01-20 12:45:04 -05:00
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.btn-blue {
|
|
|
|
|
--tw-bg-opacity: 1;
|
|
|
|
|
background-color: rgb(59 130 246 / var(--tw-bg-opacity));
|
|
|
|
|
--tw-text-opacity: 1;
|
|
|
|
|
color: rgb(255 255 255 / var(--tw-text-opacity));
|
2023-01-31 15:37:49 +01:00
|
|
|
border-radius: 0.25rem;
|
|
|
|
|
padding: 0.5rem 1rem;
|
|
|
|
|
font-weight: 700;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
.btn-blue:hover {
|
|
|
|
|
--tw-bg-opacity: 1;
|
|
|
|
|
background-color: rgb(29 78 216 / var(--tw-bg-opacity));
|
|
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
.recursive-apply-a {
|
2023-01-20 12:45:04 -05:00
|
|
|
font-weight: 900;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
.recursive-apply-a {
|
|
|
|
|
font-weight: 100;
|
|
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
|
|
|
|
.recursive-apply-b {
|
2023-01-20 12:45:04 -05:00
|
|
|
font-weight: 900;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
.recursive-apply-b {
|
|
|
|
|
font-weight: 100;
|
|
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
|
|
|
|
.recursive-apply-b {
|
2023-01-20 12:45:04 -05:00
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 768px) {
|
|
|
|
|
.recursive-apply-b {
|
|
|
|
|
font-weight: 200;
|
|
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
|
|
|
|
.recursive-apply-c {
|
2023-01-20 12:45:04 -05:00
|
|
|
font-weight: 900;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
.recursive-apply-c {
|
|
|
|
|
font-weight: 100;
|
|
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
|
|
|
|
.recursive-apply-c {
|
2023-01-20 12:45:04 -05:00
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 768px) {
|
|
|
|
|
.recursive-apply-c {
|
|
|
|
|
font-weight: 200;
|
|
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
|
|
|
|
.recursive-apply-c {
|
2023-01-20 12:45:04 -05:00
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.recursive-apply-c {
|
|
|
|
|
font-weight: 300;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-31 15:37:49 +01:00
|
|
|
.use-with-other-properties-base,
|
2023-01-20 12:45:04 -05:00
|
|
|
.use-with-other-properties-component {
|
|
|
|
|
color: green;
|
|
|
|
|
font-weight: 700;
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
|
|
|
|
.add-sibling-properties {
|
2023-01-31 15:37:49 +01:00
|
|
|
padding: 2rem 1rem;
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
.add-sibling-properties:hover {
|
|
|
|
|
padding-left: 0.5rem;
|
|
|
|
|
padding-right: 0.5rem;
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.add-sibling-properties {
|
|
|
|
|
padding-left: 2.5rem;
|
|
|
|
|
padding-right: 2.5rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1280px) {
|
|
|
|
|
.add-sibling-properties:focus {
|
|
|
|
|
padding-left: 0.25rem;
|
|
|
|
|
padding-right: 0.25rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.add-sibling-properties {
|
|
|
|
|
color: green;
|
2023-01-31 15:37:49 +01:00
|
|
|
padding-top: 3px;
|
2023-01-20 12:45:04 -05:00
|
|
|
font-weight: 700;
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
|
|
|
|
h1 {
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
line-height: 2rem;
|
|
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
h1 {
|
|
|
|
|
font-size: 1.875rem;
|
|
|
|
|
line-height: 2.25rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
h1 {
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
line-height: 2rem;
|
|
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
|
|
|
|
h2 {
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
line-height: 2rem;
|
|
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
h2 {
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
line-height: 2rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
h2 {
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
line-height: 2rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.important-modifier {
|
|
|
|
|
padding-left: 1rem;
|
|
|
|
|
padding-right: 1rem;
|
2023-01-31 15:37:49 +01:00
|
|
|
border-radius: 0.375rem !important;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
.important-modifier-variant {
|
|
|
|
|
padding-left: 1rem;
|
|
|
|
|
padding-right: 1rem;
|
|
|
|
|
}
|
|
|
|
|
.important-modifier-variant:hover {
|
|
|
|
|
border-radius: 0.375rem !important;
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
@keyframes spin {
|
|
|
|
|
to {
|
|
|
|
|
transform: rotate(360deg);
|
|
|
|
|
}
|
2023-01-19 11:42:52 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
.foo {
|
2023-01-31 15:37:49 +01:00
|
|
|
animation: 1s linear infinite spin;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
@keyframes pulse {
|
|
|
|
|
50% {
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.bar {
|
2023-01-31 15:37:49 +01:00
|
|
|
animation: 2s cubic-bezier(0.4, 0, 0.6, 1) infinite pulse !important;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
`)
|
2023-02-17 20:21:22 +01:00
|
|
|
oxide.expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.basic-example {
|
|
|
|
|
background-color: #3b82f6;
|
|
|
|
|
border-radius: 0.375rem;
|
|
|
|
|
padding: 0.5rem 1rem;
|
|
|
|
|
}
|
|
|
|
|
.class-order {
|
|
|
|
|
padding: 1rem 0.25rem 1.75rem 0.75rem;
|
|
|
|
|
}
|
|
|
|
|
.with-additional-properties {
|
|
|
|
|
text-align: right;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
.variants {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
.variants:hover {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.variants:focus {
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.variants {
|
|
|
|
|
font-weight: 300;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1280px) {
|
|
|
|
|
.variants:focus {
|
|
|
|
|
font-weight: 900;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.only-variants:hover {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.only-variants:focus {
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.only-variants {
|
|
|
|
|
font-weight: 300;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1280px) {
|
|
|
|
|
.only-variants:focus {
|
|
|
|
|
font-weight: 900;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.group:hover .apply-group-variant {
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.group:hover .apply-group-variant {
|
|
|
|
|
text-align: left;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-05 14:39:34 -05:00
|
|
|
.apply-dark-variant:where(.dark, .dark *) {
|
2023-02-17 20:21:22 +01:00
|
|
|
text-align: center;
|
|
|
|
|
}
|
2024-01-05 14:39:34 -05:00
|
|
|
.apply-dark-variant:hover:where(.dark, .dark *) {
|
2023-02-17 20:21:22 +01:00
|
|
|
text-align: right;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
2024-01-05 14:39:34 -05:00
|
|
|
.apply-dark-variant:where(.dark, .dark *) {
|
2023-02-17 20:21:22 +01:00
|
|
|
text-align: left;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.apply-custom-utility,
|
|
|
|
|
.apply-custom-utility:hover {
|
|
|
|
|
custom: stuff;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.apply-custom-utility {
|
|
|
|
|
custom: stuff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1280px) {
|
|
|
|
|
.apply-custom-utility:focus {
|
|
|
|
|
custom: stuff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.multiple,
|
|
|
|
|
.selectors {
|
|
|
|
|
background-color: #3b82f6;
|
|
|
|
|
border-radius: 0.375rem;
|
|
|
|
|
padding: 0.5rem 1rem;
|
|
|
|
|
}
|
|
|
|
|
.multiple-variants:hover,
|
|
|
|
|
.selectors-variants:hover {
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
.multiple-variants:active,
|
|
|
|
|
.selectors-variants:active {
|
|
|
|
|
text-align: right;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.multiple-variants:focus,
|
|
|
|
|
.selectors-variants:focus {
|
|
|
|
|
text-align: left;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.group:hover .multiple-group,
|
|
|
|
|
.group:hover .selectors-group {
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.group:hover .multiple-group,
|
|
|
|
|
.group:hover .selectors-group {
|
|
|
|
|
text-align: left;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.complex-utilities {
|
|
|
|
|
--tw-ordinal: ordinal;
|
|
|
|
|
--tw-numeric-spacing: tabular-nums;
|
|
|
|
|
font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure)
|
|
|
|
|
var(--tw-numeric-spacing) var(--tw-numeric-fraction);
|
|
|
|
|
--tw-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;
|
|
|
|
|
--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color),
|
|
|
|
|
0 4px 6px -4px var(--tw-shadow-color);
|
|
|
|
|
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
|
|
|
|
var(--tw-shadow);
|
|
|
|
|
}
|
|
|
|
|
.complex-utilities:hover {
|
|
|
|
|
--tw-shadow: 0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a;
|
|
|
|
|
--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color),
|
|
|
|
|
0 8px 10px -6px var(--tw-shadow-color);
|
|
|
|
|
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
|
|
|
|
var(--tw-shadow);
|
|
|
|
|
}
|
|
|
|
|
.complex-utilities:focus {
|
|
|
|
|
--tw-numeric-fraction: diagonal-fractions;
|
|
|
|
|
font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure)
|
|
|
|
|
var(--tw-numeric-spacing) var(--tw-numeric-fraction);
|
|
|
|
|
}
|
|
|
|
|
.use-base-only-a {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.use-dependant-only-b {
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
}
|
|
|
|
|
.btn {
|
|
|
|
|
border-radius: 0.25rem;
|
|
|
|
|
padding: 0.5rem 1rem;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.btn-blue {
|
|
|
|
|
color: #fff;
|
|
|
|
|
background-color: #3b82f6;
|
|
|
|
|
border-radius: 0.25rem;
|
|
|
|
|
padding: 0.5rem 1rem;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.btn-blue:hover {
|
|
|
|
|
background-color: #1d4ed8;
|
|
|
|
|
}
|
|
|
|
|
.recursive-apply-a {
|
|
|
|
|
font-weight: 900;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
.recursive-apply-a {
|
|
|
|
|
font-weight: 100;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.recursive-apply-b {
|
|
|
|
|
font-weight: 900;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
.recursive-apply-b {
|
|
|
|
|
font-weight: 100;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.recursive-apply-b {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 768px) {
|
|
|
|
|
.recursive-apply-b {
|
|
|
|
|
font-weight: 200;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.recursive-apply-c {
|
|
|
|
|
font-weight: 900;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
.recursive-apply-c {
|
|
|
|
|
font-weight: 100;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.recursive-apply-c {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 768px) {
|
|
|
|
|
.recursive-apply-c {
|
|
|
|
|
font-weight: 200;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.recursive-apply-c {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.recursive-apply-c {
|
|
|
|
|
font-weight: 300;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.use-with-other-properties-base,
|
|
|
|
|
.use-with-other-properties-component {
|
|
|
|
|
color: green;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.add-sibling-properties {
|
|
|
|
|
padding: 2rem 1rem;
|
|
|
|
|
}
|
|
|
|
|
.add-sibling-properties:hover {
|
|
|
|
|
padding-left: 0.5rem;
|
|
|
|
|
padding-right: 0.5rem;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.add-sibling-properties {
|
|
|
|
|
padding-left: 2.5rem;
|
|
|
|
|
padding-right: 2.5rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1280px) {
|
|
|
|
|
.add-sibling-properties:focus {
|
|
|
|
|
padding-left: 0.25rem;
|
|
|
|
|
padding-right: 0.25rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.add-sibling-properties {
|
|
|
|
|
color: green;
|
|
|
|
|
padding-top: 3px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
h1 {
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
line-height: 2rem;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
h1 {
|
|
|
|
|
font-size: 1.875rem;
|
|
|
|
|
line-height: 2.25rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
h1 {
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
line-height: 2rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
h2 {
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
line-height: 2rem;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
h2 {
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
line-height: 2rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
h2 {
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
line-height: 2rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.important-modifier {
|
|
|
|
|
padding-left: 1rem;
|
|
|
|
|
padding-right: 1rem;
|
|
|
|
|
border-radius: 0.375rem !important;
|
|
|
|
|
}
|
|
|
|
|
.important-modifier-variant {
|
|
|
|
|
padding-left: 1rem;
|
|
|
|
|
padding-right: 1rem;
|
|
|
|
|
}
|
|
|
|
|
.important-modifier-variant:hover {
|
|
|
|
|
border-radius: 0.375rem !important;
|
|
|
|
|
}
|
|
|
|
|
@keyframes spin {
|
|
|
|
|
to {
|
|
|
|
|
transform: rotate(360deg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.foo {
|
|
|
|
|
animation: 1s linear infinite spin;
|
|
|
|
|
}
|
|
|
|
|
@keyframes pulse {
|
|
|
|
|
50% {
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.bar {
|
|
|
|
|
animation: 2s cubic-bezier(0.4, 0, 0.6, 1) infinite pulse !important;
|
|
|
|
|
}
|
|
|
|
|
`)
|
2023-01-20 12:45:04 -05:00
|
|
|
})
|
2021-09-03 13:48:16 +02:00
|
|
|
})
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
test('@apply error with unknown utility', async () => {
|
|
|
|
|
let config = {
|
2024-01-05 14:39:34 -05:00
|
|
|
darkMode: 'selector',
|
2023-01-20 12:45:04 -05:00
|
|
|
content: [{ raw: sharedHtml }],
|
2021-09-03 13:48:16 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.foo {
|
|
|
|
|
@apply a-utility-that-does-not-exist;
|
2021-09-03 13:48:16 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
await expect(run(input, config)).rejects.toThrowError('class does not exist')
|
|
|
|
|
})
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
test('@apply error with nested @screen', async () => {
|
|
|
|
|
let config = {
|
2024-01-05 14:39:34 -05:00
|
|
|
darkMode: 'selector',
|
2023-01-20 12:45:04 -05:00
|
|
|
content: [{ raw: sharedHtml }],
|
|
|
|
|
}
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.foo {
|
|
|
|
|
@screen md {
|
|
|
|
|
@apply text-black;
|
|
|
|
|
}
|
2021-09-03 13:48:16 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
await expect(run(input, config)).rejects.toThrowError(
|
|
|
|
|
'@apply is not supported within nested at-rules like @screen'
|
|
|
|
|
)
|
|
|
|
|
})
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
test('@apply error with nested @anyatrulehere', async () => {
|
|
|
|
|
let config = {
|
2024-01-05 14:39:34 -05:00
|
|
|
darkMode: 'selector',
|
2023-01-20 12:45:04 -05:00
|
|
|
content: [{ raw: sharedHtml }],
|
2021-09-03 13:48:16 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.foo {
|
|
|
|
|
@genie {
|
|
|
|
|
@apply text-black;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
await expect(run(input, config)).rejects.toThrowError(
|
|
|
|
|
'@apply is not supported within nested at-rules like @genie'
|
|
|
|
|
)
|
|
|
|
|
})
|
2021-09-03 13:48:16 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
test('@apply error when using .group utility', async () => {
|
|
|
|
|
let config = {
|
2024-01-05 14:39:34 -05:00
|
|
|
darkMode: 'selector',
|
2023-01-20 12:45:04 -05:00
|
|
|
content: [{ raw: '<div class="foo"></div>' }],
|
2021-09-03 13:48:16 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2021-09-08 03:03:46 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.foo {
|
|
|
|
|
@apply group;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
2022-02-10 18:06:41 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
await expect(run(input, config)).rejects.toThrowError(
|
|
|
|
|
`@apply should not be used with the 'group' utility`
|
|
|
|
|
)
|
|
|
|
|
})
|
2022-02-10 18:06:41 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
test('@apply error when using a prefixed .group utility', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
prefix: 'tw-',
|
2024-01-05 14:39:34 -05:00
|
|
|
darkMode: 'selector',
|
2023-01-20 12:45:04 -05:00
|
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
2022-02-10 18:06:41 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2022-02-10 18:06:41 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.foo {
|
|
|
|
|
@apply tw-group;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
2022-02-10 18:06:41 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
await expect(run(input, config)).rejects.toThrowError(
|
|
|
|
|
`@apply should not be used with the 'tw-group' utility`
|
|
|
|
|
)
|
|
|
|
|
})
|
2022-02-10 18:06:41 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
test('@apply error when using .peer utility', async () => {
|
|
|
|
|
let config = {
|
2024-01-05 14:39:34 -05:00
|
|
|
darkMode: 'selector',
|
2023-01-20 12:45:04 -05:00
|
|
|
content: [{ raw: '<div class="foo"></div>' }],
|
2022-02-10 18:06:41 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2022-02-10 18:06:41 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.foo {
|
|
|
|
|
@apply peer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
2021-09-08 03:03:46 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
await expect(run(input, config)).rejects.toThrowError(
|
|
|
|
|
`@apply should not be used with the 'peer' utility`
|
|
|
|
|
)
|
|
|
|
|
})
|
2021-09-08 03:03:46 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
test('@apply error when using a prefixed .peer utility', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
prefix: 'tw-',
|
2024-01-05 14:39:34 -05:00
|
|
|
darkMode: 'selector',
|
2023-01-20 12:45:04 -05:00
|
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
2021-09-08 03:03:46 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2021-09-08 03:03:46 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.foo {
|
|
|
|
|
@apply tw-peer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
await expect(run(input, config)).rejects.toThrowError(
|
|
|
|
|
`@apply should not be used with the 'tw-peer' utility`
|
|
|
|
|
)
|
|
|
|
|
})
|
2021-09-08 03:03:46 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
test('@apply classes from outside a @layer', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="foo bar baz font-bold"></div>` }],
|
2021-09-08 03:03:46 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2021-09-08 03:03:46 +02:00
|
|
|
|
|
|
|
|
.foo {
|
2023-01-20 12:45:04 -05:00
|
|
|
@apply font-bold;
|
2021-09-08 03:03:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bar {
|
2023-01-20 12:45:04 -05:00
|
|
|
@apply foo text-red-500 hover:text-green-500;
|
2021-09-08 03:03:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.baz {
|
2023-01-20 12:45:04 -05:00
|
|
|
@apply bar underline;
|
2021-09-08 03:03:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.keep-me-even-though-I-am-not-used-in-content {
|
|
|
|
|
color: green;
|
|
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2021-09-08 03:03:46 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
await run(input, config).then((result) => {
|
2023-02-17 20:21:22 +01:00
|
|
|
stable.expect(result.css).toMatchFormattedCss(css`
|
2023-01-31 15:37:49 +01:00
|
|
|
.font-bold,
|
2023-01-20 12:45:04 -05:00
|
|
|
.foo {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.bar {
|
|
|
|
|
--tw-text-opacity: 1;
|
|
|
|
|
color: rgb(239 68 68 / var(--tw-text-opacity));
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.bar:hover {
|
|
|
|
|
--tw-text-opacity: 1;
|
|
|
|
|
color: rgb(34 197 94 / var(--tw-text-opacity));
|
|
|
|
|
}
|
|
|
|
|
.baz {
|
|
|
|
|
--tw-text-opacity: 1;
|
|
|
|
|
color: rgb(239 68 68 / var(--tw-text-opacity));
|
|
|
|
|
font-weight: 700;
|
2023-01-31 15:37:49 +01:00
|
|
|
text-decoration-line: underline;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
.baz:hover {
|
|
|
|
|
--tw-text-opacity: 1;
|
|
|
|
|
color: rgb(34 197 94 / var(--tw-text-opacity));
|
|
|
|
|
}
|
|
|
|
|
.keep-me-even-though-I-am-not-used-in-content {
|
|
|
|
|
color: green;
|
|
|
|
|
}
|
|
|
|
|
`)
|
2023-02-17 20:21:22 +01:00
|
|
|
oxide.expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.font-bold,
|
|
|
|
|
.foo {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.bar {
|
|
|
|
|
color: #ef4444;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.bar:hover {
|
|
|
|
|
color: #22c55e;
|
|
|
|
|
}
|
|
|
|
|
.baz {
|
|
|
|
|
color: #ef4444;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
text-decoration-line: underline;
|
|
|
|
|
}
|
|
|
|
|
.baz:hover {
|
|
|
|
|
color: #22c55e;
|
|
|
|
|
}
|
|
|
|
|
.keep-me-even-though-I-am-not-used-in-content {
|
|
|
|
|
color: green;
|
|
|
|
|
}
|
|
|
|
|
`)
|
2023-01-20 12:45:04 -05:00
|
|
|
})
|
|
|
|
|
})
|
2021-09-08 03:03:46 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
test('@applying classes from outside a @layer respects the source order', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="foo bar baz container font-bold"></div>` }],
|
2021-09-08 03:03:46 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
2021-09-08 03:03:46 +02:00
|
|
|
.baz {
|
2023-01-20 12:45:04 -05:00
|
|
|
@apply bar underline;
|
2021-09-08 03:03:46 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@tailwind components;
|
2021-09-08 03:03:46 +02:00
|
|
|
|
|
|
|
|
.keep-me-even-though-I-am-not-used-in-content {
|
|
|
|
|
color: green;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@tailwind utilities;
|
2021-09-08 03:03:46 +02:00
|
|
|
|
|
|
|
|
.foo {
|
2023-01-20 12:45:04 -05:00
|
|
|
@apply font-bold;
|
2021-09-08 03:03:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bar {
|
2023-01-20 12:45:04 -05:00
|
|
|
@apply no-underline;
|
2021-09-08 03:03:46 +02:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2021-10-21 11:54:23 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
await run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.baz {
|
|
|
|
|
text-decoration-line: none;
|
|
|
|
|
}
|
|
|
|
|
.container {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
.container {
|
|
|
|
|
max-width: 640px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 768px) {
|
|
|
|
|
.container {
|
|
|
|
|
max-width: 768px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
.container {
|
|
|
|
|
max-width: 1024px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1280px) {
|
|
|
|
|
.container {
|
|
|
|
|
max-width: 1280px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1536px) {
|
|
|
|
|
.container {
|
|
|
|
|
max-width: 1536px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.keep-me-even-though-I-am-not-used-in-content {
|
|
|
|
|
color: green;
|
|
|
|
|
}
|
2023-01-31 15:37:49 +01:00
|
|
|
.font-bold,
|
2023-01-20 12:45:04 -05:00
|
|
|
.foo {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.bar {
|
|
|
|
|
text-decoration-line: none;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
2021-10-25 11:35:12 +02:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should remove duplicate properties when using apply with similar properties', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: 'foo' }],
|
2021-10-25 11:35:12 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind utilities;
|
2021-10-25 11:35:12 +02:00
|
|
|
|
|
|
|
|
.foo {
|
2023-01-20 12:45:04 -05:00
|
|
|
@apply absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform;
|
2021-12-10 16:08:45 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.foo {
|
|
|
|
|
--tw-translate-x: -50%;
|
|
|
|
|
--tw-translate-y: -50%;
|
|
|
|
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y))
|
|
|
|
|
rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y))
|
|
|
|
|
scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
2023-01-31 15:37:49 +01:00
|
|
|
position: absolute;
|
|
|
|
|
top: 50%;
|
|
|
|
|
left: 50%;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
2021-12-10 16:08:45 +01:00
|
|
|
})
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should apply all the definitions of a class', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
|
|
|
plugins: [],
|
|
|
|
|
}
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer utilities {
|
|
|
|
|
.aspect-w-1 {
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.aspect-w-1 {
|
|
|
|
|
--tw-aspect-w: 1;
|
|
|
|
|
}
|
2021-12-10 16:08:45 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.foo {
|
|
|
|
|
@apply aspect-w-1;
|
|
|
|
|
}
|
2021-12-10 16:08:45 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.foo {
|
|
|
|
|
--tw-aspect-w: 1;
|
2023-01-31 15:37:49 +01:00
|
|
|
position: relative;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
2021-12-17 13:39:32 +01:00
|
|
|
})
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should throw when trying to apply a direct circular dependency', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
|
|
|
plugins: [],
|
2021-12-17 13:39:32 +01:00
|
|
|
}
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.foo:not(.text-red-500) {
|
|
|
|
|
@apply text-red-500;
|
|
|
|
|
}
|
2021-12-10 16:08:45 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
return run(input, config).catch((err) => {
|
|
|
|
|
expect(err.reason).toBe(
|
|
|
|
|
'You cannot `@apply` the `text-red-500` utility here because it creates a circular dependency.'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
})
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should throw when trying to apply an indirect circular dependency', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="a"></div>` }],
|
|
|
|
|
plugins: [],
|
2021-12-10 16:08:45 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.a {
|
|
|
|
|
@apply b;
|
|
|
|
|
}
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.b {
|
|
|
|
|
@apply c;
|
|
|
|
|
}
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.c {
|
|
|
|
|
@apply a;
|
|
|
|
|
}
|
2021-12-10 16:08:45 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
return run(input, config).catch((err) => {
|
|
|
|
|
expect(err.reason).toBe(
|
|
|
|
|
'You cannot `@apply` the `a` utility here because it creates a circular dependency.'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
})
|
2021-12-10 16:08:45 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should not throw when the selector is different (but contains the base partially)', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="bg-gray-500"></div>` }],
|
|
|
|
|
plugins: [],
|
2021-12-10 16:08:45 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2021-12-10 10:45:33 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.focus\:bg-gray-500 {
|
|
|
|
|
@apply bg-gray-500;
|
|
|
|
|
}
|
|
|
|
|
`
|
2022-05-02 11:11:21 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
return run(input, config).then((result) => {
|
2023-02-17 20:21:22 +01:00
|
|
|
stable.expect(result.css).toMatchFormattedCss(css`
|
2023-01-31 15:37:49 +01:00
|
|
|
.bg-gray-500,
|
2023-01-20 12:45:04 -05:00
|
|
|
.focus\:bg-gray-500 {
|
|
|
|
|
--tw-bg-opacity: 1;
|
|
|
|
|
background-color: rgb(107 114 128 / var(--tw-bg-opacity));
|
|
|
|
|
}
|
|
|
|
|
`)
|
2023-02-17 20:21:22 +01:00
|
|
|
oxide.expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.bg-gray-500,
|
|
|
|
|
.focus\:bg-gray-500 {
|
|
|
|
|
background-color: #6b7280;
|
|
|
|
|
}
|
|
|
|
|
`)
|
2023-01-20 12:45:04 -05:00
|
|
|
})
|
|
|
|
|
})
|
2022-05-02 11:11:21 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should throw when trying to apply an indirect circular dependency with a modifier (1)', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="a"></div>` }],
|
|
|
|
|
plugins: [],
|
2022-05-02 11:11:21 -04:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2022-05-02 11:11:21 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.a {
|
|
|
|
|
@apply b;
|
|
|
|
|
}
|
2022-05-02 11:11:21 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.b {
|
|
|
|
|
@apply c;
|
|
|
|
|
}
|
2022-05-02 11:11:21 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.c {
|
|
|
|
|
@apply hover:a;
|
|
|
|
|
}
|
2022-05-02 11:11:21 -04:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).catch((err) => {
|
|
|
|
|
expect(err.reason).toBe(
|
|
|
|
|
'You cannot `@apply` the `hover:a` utility here because it creates a circular dependency.'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
})
|
2022-05-02 11:11:21 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should throw when trying to apply an indirect circular dependency with a modifier (2)', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="a"></div>` }],
|
|
|
|
|
plugins: [],
|
2022-05-02 11:11:21 -04:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2022-05-02 11:11:21 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.a {
|
|
|
|
|
@apply b;
|
|
|
|
|
}
|
2022-05-02 11:11:21 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.b {
|
|
|
|
|
@apply hover:c;
|
|
|
|
|
}
|
2022-05-02 11:11:21 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.c {
|
|
|
|
|
@apply a;
|
|
|
|
|
}
|
2022-05-02 11:11:21 -04:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2022-05-02 11:11:21 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
return run(input, config).catch((err) => {
|
|
|
|
|
expect(err.reason).toBe(
|
|
|
|
|
'You cannot `@apply` the `a` utility here because it creates a circular dependency.'
|
|
|
|
|
)
|
|
|
|
|
})
|
2022-05-02 11:11:21 -04:00
|
|
|
})
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should not throw when the circular dependency is part of a different selector (1)', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="c"></div>` }],
|
|
|
|
|
plugins: [],
|
|
|
|
|
}
|
2021-12-10 10:45:33 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind utilities;
|
2021-12-10 10:45:33 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer utilities {
|
|
|
|
|
html.dark .a,
|
|
|
|
|
.b {
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
2021-12-10 10:45:33 -05:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
html.dark .c {
|
|
|
|
|
@apply b;
|
2021-12-10 10:45:33 -05:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2021-12-17 14:49:07 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
html.dark .c {
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
2021-12-17 14:49:07 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should not throw when the circular dependency is part of a different selector (2)', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="c"></div>` }],
|
|
|
|
|
plugins: [],
|
2021-12-17 14:49:07 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind utilities;
|
2021-12-17 14:49:07 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer utilities {
|
|
|
|
|
html.dark .a,
|
|
|
|
|
.b {
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
2021-12-17 14:49:07 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
html.dark .c {
|
|
|
|
|
@apply hover:b;
|
2021-12-17 14:49:07 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
html.dark .c:hover {
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
2021-12-17 14:49:07 +01:00
|
|
|
})
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should throw when the circular dependency is part of the same selector', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="c"></div>` }],
|
|
|
|
|
plugins: [],
|
|
|
|
|
}
|
2021-12-17 14:49:07 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind utilities;
|
2021-12-17 14:49:07 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer utilities {
|
|
|
|
|
html.dark .a,
|
|
|
|
|
html.dark .b {
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-17 14:49:07 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
html.dark .c {
|
|
|
|
|
@apply hover:b;
|
|
|
|
|
}
|
|
|
|
|
`
|
2021-12-17 14:49:07 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
return run(input, config).catch((err) => {
|
|
|
|
|
expect(err.reason).toBe(
|
|
|
|
|
'You cannot `@apply` the `hover:b` utility here because it creates a circular dependency.'
|
|
|
|
|
)
|
|
|
|
|
})
|
2021-12-17 14:49:07 +01:00
|
|
|
})
|
2021-12-17 18:40:20 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('rules with vendor prefixes are still separate when optimizing defaults rules', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
experimental: { optimizeUniversalDefaults: true },
|
|
|
|
|
content: [{ raw: html`<div class="border"></div>` }],
|
|
|
|
|
corePlugins: { preflight: false },
|
|
|
|
|
}
|
2021-12-17 18:40:20 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind base;
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2021-12-17 18:40:20 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
input[type='range']::-moz-range-thumb {
|
|
|
|
|
@apply border;
|
|
|
|
|
}
|
2021-12-17 18:40:20 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2021-12-17 18:40:20 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
input[type='range']::-moz-range-thumb {
|
|
|
|
|
border-width: 1px;
|
2021-12-17 18:40:20 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
.border {
|
|
|
|
|
border-width: 1px;
|
2021-12-17 18:40:20 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should be possible to apply user css', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div></div>` }],
|
|
|
|
|
plugins: [],
|
2021-12-17 18:40:20 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2021-12-17 18:40:20 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.foo {
|
|
|
|
|
color: red;
|
2021-12-17 18:40:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bar {
|
2023-01-20 12:45:04 -05:00
|
|
|
@apply foo;
|
2021-12-17 18:40:20 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2021-12-17 18:40:20 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
2023-01-31 15:37:49 +01:00
|
|
|
.foo,
|
2023-01-20 12:45:04 -05:00
|
|
|
.bar {
|
2021-12-17 18:40:20 +01:00
|
|
|
color: red;
|
|
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`)
|
|
|
|
|
})
|
2021-12-17 18:40:20 +01:00
|
|
|
})
|
2022-01-04 05:23:10 -08:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should not be possible to apply user css with variants', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div></div>` }],
|
|
|
|
|
plugins: [],
|
2022-01-04 05:23:10 -08:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
|
|
|
|
|
2022-01-04 05:23:10 -08:00
|
|
|
.foo {
|
|
|
|
|
color: red;
|
2022-01-06 13:27:14 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.bar {
|
|
|
|
|
@apply hover:foo;
|
2022-01-06 13:27:14 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2022-01-06 13:27:14 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
return run(input, config).catch((err) => {
|
|
|
|
|
expect(err.reason).toBe(
|
|
|
|
|
'The `hover:foo` class does not exist. If `hover:foo` is a custom class, make sure it is defined within a `@layer` directive.'
|
|
|
|
|
)
|
|
|
|
|
})
|
2022-01-06 13:27:14 +01:00
|
|
|
})
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should not apply unrelated siblings when applying something from within atrules', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="foo bar something-unrelated"></div>` }],
|
|
|
|
|
plugins: [],
|
|
|
|
|
}
|
2022-01-06 13:27:14 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@tailwind utilities;
|
2022-01-06 13:27:14 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer components {
|
|
|
|
|
.foo {
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
@apply bar;
|
|
|
|
|
}
|
2022-01-06 13:27:14 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.bar {
|
|
|
|
|
color: green;
|
|
|
|
|
}
|
2022-01-06 13:27:14 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@supports (a: b) {
|
|
|
|
|
.bar {
|
|
|
|
|
color: blue;
|
|
|
|
|
}
|
2022-01-06 13:27:14 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.something-unrelated {
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
2022-01-06 13:27:14 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.foo {
|
|
|
|
|
color: green;
|
2023-01-31 15:37:49 +01:00
|
|
|
font-weight: bold;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
@supports (a: b) {
|
|
|
|
|
.foo {
|
2023-01-31 15:37:49 +01:00
|
|
|
color: #00f;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.bar {
|
|
|
|
|
color: green;
|
|
|
|
|
}
|
|
|
|
|
@supports (a: b) {
|
|
|
|
|
.bar {
|
2023-01-31 15:37:49 +01:00
|
|
|
color: #00f;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
.something-unrelated {
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
2022-01-06 13:27:14 +01:00
|
|
|
})
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should be possible to apply user css without tailwind directives', () => {
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
let config = {
|
2023-01-20 12:45:04 -05:00
|
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
plugins: [],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
2023-01-20 12:45:04 -05:00
|
|
|
.bop {
|
|
|
|
|
color: red;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
.bar {
|
|
|
|
|
background-color: blue;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
.foo {
|
|
|
|
|
@apply bar bop absolute;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
2023-01-20 12:45:04 -05:00
|
|
|
.bop {
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
color: red;
|
|
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
.bar {
|
2023-01-31 15:37:49 +01:00
|
|
|
background-color: #00f;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
.foo {
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
color: red;
|
2023-01-31 15:37:49 +01:00
|
|
|
background-color: #00f;
|
|
|
|
|
position: absolute;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should be possible to apply a class from another rule with multiple selectors (2 classes)', () => {
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
let config = {
|
2023-01-20 12:45:04 -05:00
|
|
|
content: [{ raw: html`<div class="c"></div>` }],
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
plugins: [],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
2023-01-20 12:45:04 -05:00
|
|
|
@tailwind utilities;
|
|
|
|
|
@layer utilities {
|
|
|
|
|
.a,
|
|
|
|
|
.b {
|
|
|
|
|
@apply underline;
|
|
|
|
|
}
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.c {
|
|
|
|
|
@apply b;
|
|
|
|
|
}
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
2023-01-20 12:45:04 -05:00
|
|
|
.c {
|
|
|
|
|
text-decoration-line: underline;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should be possible to apply a class from another rule with multiple selectors (1 class, 1 tag)', () => {
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
let config = {
|
2023-01-20 12:45:04 -05:00
|
|
|
content: [{ raw: html`<div class="c"></div>` }],
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
plugins: [],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
2023-01-20 12:45:04 -05:00
|
|
|
@tailwind utilities;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@layer utilities {
|
|
|
|
|
span,
|
|
|
|
|
.b {
|
|
|
|
|
@apply underline;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.c {
|
|
|
|
|
@apply b;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
2023-01-20 12:45:04 -05:00
|
|
|
span,
|
2023-01-31 15:37:49 +01:00
|
|
|
.b,
|
2023-01-20 12:45:04 -05:00
|
|
|
.c {
|
|
|
|
|
text-decoration-line: underline;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should be possible to apply a class from another rule with multiple selectors (1 class, 1 id)', () => {
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
let config = {
|
2023-01-20 12:45:04 -05:00
|
|
|
content: [{ raw: html`<div class="c"></div>` }],
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
plugins: [],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
2023-01-20 12:45:04 -05:00
|
|
|
@tailwind utilities;
|
|
|
|
|
@layer utilities {
|
|
|
|
|
#a,
|
|
|
|
|
.b {
|
|
|
|
|
@apply underline;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.c {
|
|
|
|
|
@apply b;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
2023-01-20 12:45:04 -05:00
|
|
|
#a,
|
2023-01-31 15:37:49 +01:00
|
|
|
.b,
|
2023-01-20 12:45:04 -05:00
|
|
|
.c {
|
|
|
|
|
text-decoration-line: underline;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('multiple instances', () => {
|
|
|
|
|
it('should be possible to apply multiple "instances" of the same class', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`` }],
|
|
|
|
|
plugins: [],
|
|
|
|
|
corePlugins: { preflight: false },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
.a {
|
2023-01-20 12:45:04 -05:00
|
|
|
@apply b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.b {
|
|
|
|
|
@apply uppercase;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.b {
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
color: red;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
2023-01-31 15:37:49 +01:00
|
|
|
.a,
|
2023-01-20 12:45:04 -05:00
|
|
|
.b {
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should be possible to apply a combination of multiple "instances" of the same class', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`` }],
|
|
|
|
|
plugins: [],
|
|
|
|
|
corePlugins: { preflight: false },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
|
|
|
|
.a {
|
|
|
|
|
@apply b;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.b {
|
2023-01-20 12:45:04 -05:00
|
|
|
@apply uppercase;
|
|
|
|
|
color: red;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
2023-01-31 15:37:49 +01:00
|
|
|
.a,
|
2023-01-20 12:45:04 -05:00
|
|
|
.b {
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should generate the same output, even if it was used in a @layer', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="a b"></div>` }],
|
|
|
|
|
plugins: [],
|
|
|
|
|
corePlugins: { preflight: false },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
|
|
|
|
|
@layer components {
|
|
|
|
|
.a {
|
|
|
|
|
@apply b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.b {
|
|
|
|
|
@apply uppercase;
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
2023-01-31 15:37:49 +01:00
|
|
|
.a,
|
2023-01-20 12:45:04 -05:00
|
|
|
.b {
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should be possible to apply a combination of multiple "instances" of the same class (defined in a layer)', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="a b"></div>` }],
|
|
|
|
|
plugins: [],
|
|
|
|
|
corePlugins: { preflight: false },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
|
|
|
|
|
@layer components {
|
|
|
|
|
.a {
|
|
|
|
|
color: red;
|
|
|
|
|
@apply b;
|
|
|
|
|
color: blue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.b {
|
|
|
|
|
@apply text-green-500;
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
2023-02-17 20:21:22 +01:00
|
|
|
stable.expect(result.css).toMatchFormattedCss(css`
|
2023-01-20 12:45:04 -05:00
|
|
|
.a {
|
|
|
|
|
color: red;
|
|
|
|
|
--tw-text-opacity: 1;
|
|
|
|
|
color: rgb(34 197 94 / var(--tw-text-opacity));
|
2023-01-31 15:37:49 +01:00
|
|
|
color: #00f;
|
2023-01-20 12:45:04 -05:00
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
.b {
|
|
|
|
|
--tw-text-opacity: 1;
|
|
|
|
|
color: rgb(34 197 94 / var(--tw-text-opacity));
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
`)
|
2023-02-17 20:21:22 +01:00
|
|
|
oxide.expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.a {
|
|
|
|
|
color: red;
|
|
|
|
|
color: #22c55e;
|
|
|
|
|
color: #00f;
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
.b {
|
|
|
|
|
color: #22c55e;
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
`)
|
2023-01-20 12:45:04 -05:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should properly maintain the order', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`` }],
|
|
|
|
|
plugins: [],
|
|
|
|
|
corePlugins: { preflight: false },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
|
|
|
|
h2 {
|
|
|
|
|
@apply text-xl;
|
|
|
|
|
@apply lg:text-3xl;
|
|
|
|
|
@apply sm:text-2xl;
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
h2 {
|
|
|
|
|
font-size: 1.25rem;
|
|
|
|
|
line-height: 1.75rem;
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 1024px) {
|
|
|
|
|
h2 {
|
|
|
|
|
font-size: 1.875rem;
|
|
|
|
|
line-height: 2.25rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
h2 {
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
line-height: 2rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('apply can emit defaults in isolated environments without @tailwind directives', () => {
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
let config = {
|
2023-01-20 12:45:04 -05:00
|
|
|
experimental: { optimizeUniversalDefaults: true },
|
|
|
|
|
|
|
|
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
2023-01-20 12:45:04 -05:00
|
|
|
.foo {
|
|
|
|
|
@apply focus:rotate-90;
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
2023-01-20 12:45:04 -05:00
|
|
|
.foo:focus {
|
|
|
|
|
--tw-rotate: 90deg;
|
|
|
|
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y))
|
|
|
|
|
rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y))
|
|
|
|
|
scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('apply does not emit defaults in isolated environments without optimizeUniversalDefaults', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
experimental: { optimizeUniversalDefaults: false },
|
|
|
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
|
|
|
corePlugins: { preflight: false },
|
|
|
|
|
}
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind base;
|
|
|
|
|
|
|
|
|
|
.foo {
|
|
|
|
|
@apply focus:rotate-90;
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
return expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
${defaults}
|
|
|
|
|
.foo:focus {
|
|
|
|
|
--tw-rotate: 90deg;
|
|
|
|
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y))
|
|
|
|
|
rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y))
|
|
|
|
|
scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
Ensure `@apply` works consistently with or without `@layer` (#6938)
* partition nodes as soon as possible
Time to write another story on `@apply`...
When we write code like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
Then we create 2 Nodes in our context to keep track of. One has
identifier `a`, the other has identifier `b`. However, when we have an
`@apply` and it contains multiple declarations/atrules, then we have to
split up the (aka partition) node into multiple nodes so that we can
guarantee the correct expected sort order.
This means that the above example technically looks like this:
```css
.a {
@apply b;
}
.b {
@apply uppercase;
}
.b {
color: red;
}
```
If this was your input, then we would still have 1 node for identifier
'a', but we would have 2 nodes for identifier 'b'.
As mentioned earlier, this is important to guarantee the correct order,
here is an example:
```css
.b {
@apply md:font-bold xl:font-normal; /* Here we can sort by our
internal rules. This means that the `md` comes before `xl`. */
}
```
... however
```css
.b {
@apply xl:font-normal; /* This now exists _before_ the example below */
}
.b {
@apply md:font-bold; /* Because we respect the order of the user's css */
}
```
So to guarantee the order when doing this:
```css
.b {
@apply xl:font-normal;
@apply lg:font-normal;
}
```
We also split this up into 2 nodes like this:
```css
.b {
@apply xl:font-normal;
}
.b {
@apply lg:font-normal;
}
```
The tricky part is that now only 1 empty `.b` node exists in our context
because we partitioned the orginal node into multiple nodes and moved
the children to the new nodes and because they are new nodes it means
that they have a different identity.
This partitioning used to happen in the expandApplyAtRules code, but
this is a bit too late because the context has already been filled at
this time. Instead, we move the code more to the front, as if you wrote
those separated blocks yourself. Now the code to inject those nodes into
the context happens in a single spot instead of multiple places.
Another good part about this is that we have better consistency between
each layer because it turns out that these two examples generated
different results...
```css
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
```
... is different compared to:
```css
@tailwind components;
@layer components {
.a {
@apply b;
}
.b {
@apply uppercase;
color: red;
}
}
```
Even if both `a` and `b` are being used in one of your content paths...
Yeah.. *sigh*
* add more `@apply` related tests
* update changelog
* remove support for basic nesting (leftover)
* remove leftover todo
This has been fixed already
2022-01-07 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should work outside of layer', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="input-text"></div>` }],
|
|
|
|
|
corePlugins: { preflight: false },
|
2022-01-04 11:29:33 -05:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
.input-text {
|
|
|
|
|
@apply bg-white;
|
|
|
|
|
background-color: red;
|
2022-01-04 11:29:33 -05:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2022-01-07 11:39:45 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let result
|
|
|
|
|
result = await run(input, config)
|
2022-01-07 11:39:45 -05:00
|
|
|
|
2023-02-17 20:21:22 +01:00
|
|
|
stable.expect(result.css).toMatchFormattedCss(css`
|
2023-01-20 12:45:04 -05:00
|
|
|
.input-text {
|
|
|
|
|
--tw-bg-opacity: 1;
|
|
|
|
|
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
|
|
|
|
background-color: red;
|
|
|
|
|
}
|
|
|
|
|
`)
|
2023-02-17 20:21:22 +01:00
|
|
|
oxide.expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.input-text {
|
|
|
|
|
background-color: red;
|
|
|
|
|
}
|
|
|
|
|
`)
|
2022-01-07 11:39:45 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
result = await run(input, config)
|
2022-01-07 11:39:45 -05:00
|
|
|
|
2023-02-17 20:21:22 +01:00
|
|
|
stable.expect(result.css).toMatchFormattedCss(css`
|
2023-01-20 12:45:04 -05:00
|
|
|
.input-text {
|
|
|
|
|
--tw-bg-opacity: 1;
|
|
|
|
|
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
|
|
|
|
background-color: red;
|
2022-01-04 11:29:33 -05:00
|
|
|
}
|
|
|
|
|
`)
|
2023-02-17 20:21:22 +01:00
|
|
|
oxide.expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.input-text {
|
|
|
|
|
background-color: red;
|
|
|
|
|
}
|
|
|
|
|
`)
|
2022-01-04 11:29:33 -05:00
|
|
|
})
|
2022-01-10 12:36:14 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should work in layer', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="input-text"></div>` }],
|
|
|
|
|
corePlugins: { preflight: false },
|
2022-01-10 12:36:14 -05:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@layer components {
|
|
|
|
|
.input-text {
|
|
|
|
|
@apply bg-white;
|
|
|
|
|
background-color: red;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
2022-01-10 12:36:14 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
await run(input, config)
|
|
|
|
|
const result = await run(input, config)
|
2022-01-10 12:36:14 -05:00
|
|
|
|
2023-02-17 20:21:22 +01:00
|
|
|
stable.expect(result.css).toMatchFormattedCss(css`
|
2022-01-10 12:36:14 -05:00
|
|
|
.input-text {
|
2023-01-20 12:45:04 -05:00
|
|
|
--tw-bg-opacity: 1;
|
|
|
|
|
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
2022-01-10 12:36:14 -05:00
|
|
|
background-color: red;
|
|
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`)
|
2023-02-17 20:21:22 +01:00
|
|
|
oxide.expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.input-text {
|
|
|
|
|
background-color: red;
|
|
|
|
|
}
|
|
|
|
|
`)
|
2023-01-20 12:45:04 -05:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('apply partitioning works with media queries', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`` }],
|
|
|
|
|
corePlugins: { preflight: false },
|
2022-01-10 12:36:14 -05:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind base;
|
|
|
|
|
@layer base {
|
|
|
|
|
html,
|
|
|
|
|
body {
|
|
|
|
|
@apply text-green-600;
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
}
|
2022-01-10 12:36:14 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
@media print {
|
|
|
|
|
html,
|
|
|
|
|
body {
|
|
|
|
|
@apply text-red-600;
|
|
|
|
|
font-size: 2rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
2022-01-10 12:45:34 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
await run(input, config)
|
|
|
|
|
const result = await run(input, config)
|
2022-01-10 12:45:34 -05:00
|
|
|
|
2023-02-17 20:21:22 +01:00
|
|
|
stable.expect(result.css).toMatchFormattedCss(css`
|
2022-01-10 12:45:34 -05:00
|
|
|
html,
|
|
|
|
|
body {
|
2023-01-20 12:45:04 -05:00
|
|
|
--tw-text-opacity: 1;
|
|
|
|
|
color: rgb(22 163 74 / var(--tw-text-opacity));
|
2022-01-10 12:45:34 -05:00
|
|
|
font-size: 1rem;
|
|
|
|
|
}
|
|
|
|
|
@media print {
|
|
|
|
|
html,
|
|
|
|
|
body {
|
2023-01-20 12:45:04 -05:00
|
|
|
--tw-text-opacity: 1;
|
|
|
|
|
color: rgb(220 38 38 / var(--tw-text-opacity));
|
2022-01-10 12:45:34 -05:00
|
|
|
font-size: 2rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
${defaults}
|
|
|
|
|
`)
|
2023-02-17 20:21:22 +01:00
|
|
|
oxide.expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
html,
|
|
|
|
|
body {
|
|
|
|
|
color: #16a34a;
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
}
|
|
|
|
|
@media print {
|
|
|
|
|
html,
|
|
|
|
|
body {
|
|
|
|
|
color: #dc2626;
|
|
|
|
|
font-size: 2rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
${defaults}
|
|
|
|
|
`)
|
2023-01-20 12:45:04 -05:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should be possible to use apply in plugins', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="a b"></div>` }],
|
|
|
|
|
corePlugins: { preflight: false },
|
|
|
|
|
plugins: [
|
|
|
|
|
function ({ addComponents }) {
|
|
|
|
|
addComponents({
|
|
|
|
|
'.a': {
|
|
|
|
|
color: 'red',
|
|
|
|
|
},
|
|
|
|
|
'.b': {
|
|
|
|
|
'@apply a': {},
|
|
|
|
|
color: 'blue',
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.a {
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
.b {
|
|
|
|
|
color: red;
|
2023-01-31 15:37:49 +01:00
|
|
|
color: #00f;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
2022-01-10 12:45:34 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should apply using the updated user CSS when the source has changed', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div></div>` }],
|
|
|
|
|
plugins: [],
|
2022-01-10 12:45:34 -05:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let inputBefore = css`
|
|
|
|
|
.foo {
|
|
|
|
|
color: green;
|
2022-01-10 12:45:34 -05:00
|
|
|
}
|
2022-01-27 13:16:44 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.bar {
|
|
|
|
|
@apply foo;
|
2022-01-27 13:16:44 +01:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
2022-01-27 13:16:44 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let inputAfter = css`
|
|
|
|
|
.foo {
|
2022-01-27 13:16:44 +01:00
|
|
|
color: red;
|
|
|
|
|
}
|
2022-02-25 08:35:22 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
.bar {
|
|
|
|
|
@apply foo;
|
|
|
|
|
}
|
|
|
|
|
`
|
2022-02-25 08:35:22 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let result = await run(inputBefore, config)
|
2022-02-25 08:35:22 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
2023-01-31 15:37:49 +01:00
|
|
|
.foo,
|
2023-01-20 12:45:04 -05:00
|
|
|
.bar {
|
|
|
|
|
color: green;
|
|
|
|
|
}
|
|
|
|
|
`)
|
2022-02-25 08:35:22 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
result = await run(inputAfter, config)
|
2022-02-25 08:35:22 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
2023-01-31 15:37:49 +01:00
|
|
|
.foo,
|
2023-01-20 12:45:04 -05:00
|
|
|
.bar {
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
2022-02-25 08:35:22 -05:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('apply + layer utilities + selector variants (like group) + important selector', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
important: '#myselector',
|
|
|
|
|
content: [{ raw: html`<div class="custom-utility"></div>` }],
|
|
|
|
|
plugins: [],
|
2022-02-25 08:35:22 -05:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind utilities;
|
|
|
|
|
@layer utilities {
|
|
|
|
|
.custom-utility {
|
|
|
|
|
@apply font-normal group-hover:underline;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
2022-05-09 14:20:36 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let result = await run(input, config)
|
2022-05-09 14:20:36 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
2023-03-21 12:50:44 -04:00
|
|
|
#myselector :is(.custom-utility) {
|
2023-01-20 12:45:04 -05:00
|
|
|
font-weight: 400;
|
2022-05-09 14:20:36 -04:00
|
|
|
}
|
2023-03-21 12:50:44 -04:00
|
|
|
#myselector :is(.group:hover .custom-utility) {
|
2023-01-20 12:45:04 -05:00
|
|
|
text-decoration-line: underline;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
2022-05-09 14:20:36 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('apply + user CSS + selector variants (like group) + important selector (1)', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
important: '#myselector',
|
|
|
|
|
content: [{ raw: html`<div class="custom-utility"></div>` }],
|
|
|
|
|
plugins: [],
|
2022-05-09 14:20:36 -04:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
.custom-utility {
|
|
|
|
|
@apply font-normal group-hover:underline;
|
|
|
|
|
}
|
|
|
|
|
`
|
2022-05-09 14:20:36 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let result = await run(input, config)
|
2022-05-09 14:20:36 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.custom-utility {
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
}
|
|
|
|
|
.group:hover .custom-utility {
|
|
|
|
|
text-decoration-line: underline;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
2022-05-09 14:20:36 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('apply + user CSS + selector variants (like group) + important selector (2)', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
important: '#myselector',
|
|
|
|
|
content: [{ raw: html`<div class="custom-utility"></div>` }],
|
|
|
|
|
plugins: [],
|
2022-05-09 14:20:36 -04:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
#myselector .custom-utility {
|
|
|
|
|
@apply font-normal group-hover:underline;
|
|
|
|
|
}
|
|
|
|
|
`
|
2022-05-09 14:20:36 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let result = await run(input, config)
|
2022-05-09 14:20:36 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
#myselector .custom-utility {
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
}
|
|
|
|
|
.group:hover #myselector .custom-utility {
|
|
|
|
|
text-decoration-line: underline;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
2022-05-09 14:20:36 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('can apply user utilities that start with a dash', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="foo-1 -foo-1 new-class"></div>` }],
|
|
|
|
|
plugins: [],
|
2022-05-09 14:20:36 -04:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind utilities;
|
|
|
|
|
@layer utilities {
|
|
|
|
|
.foo-1 {
|
|
|
|
|
margin: 10px;
|
|
|
|
|
}
|
|
|
|
|
.-foo-1 {
|
|
|
|
|
margin: -15px;
|
|
|
|
|
}
|
|
|
|
|
.new-class {
|
|
|
|
|
@apply -foo-1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
2022-08-04 14:24:17 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let result = await run(input, config)
|
2022-08-04 14:24:17 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
2022-08-04 14:24:17 -04:00
|
|
|
.foo-1 {
|
|
|
|
|
margin: 10px;
|
|
|
|
|
}
|
2023-01-31 15:37:49 +01:00
|
|
|
.-foo-1,
|
2022-08-04 14:24:17 -04:00
|
|
|
.new-class {
|
2023-01-20 12:45:04 -05:00
|
|
|
margin: -15px;
|
2022-08-04 14:24:17 -04:00
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`)
|
|
|
|
|
})
|
2022-08-04 14:24:17 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('can apply joined classes when using elements', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="foo-1 -foo-1 new-class"></div>` }],
|
|
|
|
|
plugins: [],
|
2022-08-04 14:24:17 -04:00
|
|
|
}
|
2022-08-15 14:43:41 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
.foo.bar {
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
.bar.foo {
|
|
|
|
|
color: green;
|
|
|
|
|
}
|
|
|
|
|
header:nth-of-type(odd) {
|
|
|
|
|
@apply foo;
|
|
|
|
|
}
|
|
|
|
|
header::after {
|
|
|
|
|
@apply foo;
|
|
|
|
|
}
|
|
|
|
|
main {
|
|
|
|
|
@apply foo bar;
|
|
|
|
|
}
|
|
|
|
|
footer {
|
|
|
|
|
@apply bar;
|
|
|
|
|
}
|
|
|
|
|
`
|
2022-08-15 14:43:41 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let result = await run(input, config)
|
2022-08-15 14:43:41 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.foo.bar {
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
.bar.foo {
|
|
|
|
|
color: green;
|
|
|
|
|
}
|
2023-01-31 15:37:49 +01:00
|
|
|
header:nth-of-type(2n + 1).bar {
|
2023-01-20 12:45:04 -05:00
|
|
|
color: red;
|
|
|
|
|
}
|
2023-01-31 15:37:49 +01:00
|
|
|
header.bar:nth-of-type(2n + 1) {
|
2023-01-20 12:45:04 -05:00
|
|
|
color: green;
|
|
|
|
|
}
|
2023-01-31 15:37:49 +01:00
|
|
|
header.bar:after,
|
|
|
|
|
main.bar,
|
|
|
|
|
main.foo,
|
2023-01-20 12:45:04 -05:00
|
|
|
footer.foo {
|
|
|
|
|
color: red;
|
|
|
|
|
color: green;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
2022-08-15 14:43:41 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should not replace multiple instances of the same class in a single selector', async () => {
|
|
|
|
|
// NOTE: This test is non-normative and is not part of the spec of how `@apply` works per-se
|
|
|
|
|
// It describes how it currently works because the "correct" way produces a combinatorial explosion
|
|
|
|
|
// of selectors that is not easily doable
|
|
|
|
|
let config = {
|
|
|
|
|
content: [{ raw: html`<div class="foo-1 -foo-1 new-class"></div>` }],
|
|
|
|
|
plugins: [],
|
2022-08-15 14:43:41 -04:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
.foo + .foo {
|
|
|
|
|
color: blue;
|
|
|
|
|
}
|
|
|
|
|
.bar + .bar {
|
|
|
|
|
color: fuchsia;
|
|
|
|
|
}
|
|
|
|
|
header {
|
|
|
|
|
@apply foo;
|
|
|
|
|
}
|
|
|
|
|
main {
|
|
|
|
|
@apply foo bar;
|
|
|
|
|
}
|
|
|
|
|
footer {
|
|
|
|
|
@apply bar;
|
|
|
|
|
}
|
|
|
|
|
`
|
2022-08-15 14:43:41 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let result = await run(input, config)
|
2022-08-15 14:43:41 -04:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.foo + .foo {
|
2023-01-31 15:37:49 +01:00
|
|
|
color: #00f;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
.bar + .bar {
|
2023-01-31 15:37:49 +01:00
|
|
|
color: #f0f;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
2023-01-31 15:37:49 +01:00
|
|
|
header + .foo,
|
2023-01-20 12:45:04 -05:00
|
|
|
main + .foo {
|
2023-01-31 15:37:49 +01:00
|
|
|
color: #00f;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
2023-01-31 15:37:49 +01:00
|
|
|
main + .bar,
|
2023-01-20 12:45:04 -05:00
|
|
|
footer + .bar {
|
2023-01-31 15:37:49 +01:00
|
|
|
color: #f0f;
|
2023-01-20 12:45:04 -05:00
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
2022-11-03 12:20:38 +01:00
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
it('should maintain the correct selector when applying other utilities', () => {
|
|
|
|
|
let config = {
|
|
|
|
|
content: [
|
|
|
|
|
{
|
|
|
|
|
raw: html`
|
|
|
|
|
<div>
|
|
|
|
|
<div class="check"></div>
|
|
|
|
|
</div>
|
|
|
|
|
`,
|
|
|
|
|
},
|
|
|
|
|
],
|
2022-11-03 12:20:38 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:45:04 -05:00
|
|
|
let input = css`
|
|
|
|
|
@tailwind utilities;
|
2022-11-03 12:20:38 +01:00
|
|
|
|
2022-11-09 16:41:16 -05:00
|
|
|
.foo:hover.bar .baz {
|
2023-01-20 12:45:04 -05:00
|
|
|
@apply bg-black;
|
2022-11-03 12:20:38 +01:00
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-09 16:41:16 -05:00
|
|
|
.foo:hover.bar > .baz {
|
2023-01-20 12:45:04 -05:00
|
|
|
@apply bg-black;
|
2022-11-03 12:20:38 +01:00
|
|
|
color: red;
|
|
|
|
|
}
|
2023-01-20 12:45:04 -05:00
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
2023-02-17 20:21:22 +01:00
|
|
|
stable.expect(result.css).toMatchFormattedCss(css`
|
2023-01-31 15:37:49 +01:00
|
|
|
.foo:hover.bar .baz,
|
2023-01-20 12:45:04 -05:00
|
|
|
.foo:hover.bar > .baz {
|
|
|
|
|
--tw-bg-opacity: 1;
|
|
|
|
|
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
|
|
|
|
color: red;
|
|
|
|
|
}
|
|
|
|
|
`)
|
2023-02-17 20:21:22 +01:00
|
|
|
oxide.expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.foo:hover.bar .baz,
|
|
|
|
|
.foo:hover.bar > .baz {
|
|
|
|
|
color: red;
|
|
|
|
|
background-color: #000;
|
|
|
|
|
}
|
|
|
|
|
`)
|
2023-01-20 12:45:04 -05:00
|
|
|
})
|
2022-11-03 12:20:38 +01:00
|
|
|
})
|
2023-03-29 15:37:26 -04:00
|
|
|
|
|
|
|
|
it('pseudo elements inside apply are moved outside of :is() or :has()', () => {
|
|
|
|
|
let config = {
|
2024-01-05 14:39:34 -05:00
|
|
|
darkMode: 'selector',
|
2023-03-29 15:37:26 -04:00
|
|
|
content: [
|
|
|
|
|
{
|
|
|
|
|
raw: html` <div class="foo bar baz qux steve bob"></div> `,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
|
|
|
|
.foo::before {
|
|
|
|
|
@apply dark:bg-black/100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bar::before {
|
|
|
|
|
@apply rtl:dark:bg-black/100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.baz::before {
|
|
|
|
|
@apply rtl:dark:hover:bg-black/100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.qux::file-selector-button {
|
|
|
|
|
@apply rtl:dark:hover:bg-black/100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.steve::before {
|
|
|
|
|
@apply rtl:hover:dark:bg-black/100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bob::file-selector-button {
|
|
|
|
|
@apply rtl:hover:dark:bg-black/100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.foo::before {
|
|
|
|
|
@apply [:has([dir="rtl"]_&)]:hover:bg-black/100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bar::file-selector-button {
|
|
|
|
|
@apply [:has([dir="rtl"]_&)]:hover:bg-black/100;
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
2024-01-05 14:39:34 -05:00
|
|
|
.foo:where(.dark, .dark *)::before,
|
|
|
|
|
.bar:where(.dark, .dark *):where([dir='rtl'], [dir='rtl'] *)::before,
|
|
|
|
|
.baz:hover:where(.dark, .dark *):where([dir='rtl'], [dir='rtl'] *)::before {
|
2023-03-29 15:37:26 -04:00
|
|
|
background-color: #000;
|
|
|
|
|
}
|
2024-01-05 14:39:34 -05:00
|
|
|
.qux:where(.dark, .dark *):where([dir='rtl'], [dir='rtl'] *)::file-selector-button:hover {
|
2023-03-29 15:37:26 -04:00
|
|
|
background-color: #000;
|
|
|
|
|
}
|
2024-01-05 14:39:34 -05:00
|
|
|
.steve:where(.dark, .dark *):hover:where([dir='rtl'], [dir='rtl'] *):before {
|
2023-03-29 15:37:26 -04:00
|
|
|
background-color: #000;
|
|
|
|
|
}
|
2024-01-05 14:39:34 -05:00
|
|
|
.bob:where(.dark, .dark *):hover:where([dir='rtl'], [dir='rtl'] *)::file-selector-button {
|
2023-03-29 15:37:26 -04:00
|
|
|
background-color: #000;
|
|
|
|
|
}
|
|
|
|
|
:has([dir='rtl'] .foo:hover):before {
|
|
|
|
|
background-color: #000;
|
|
|
|
|
}
|
|
|
|
|
:has([dir='rtl'] .bar)::file-selector-button:hover {
|
|
|
|
|
background-color: #000;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
2023-04-04 12:46:20 -04:00
|
|
|
|
2023-04-07 10:45:47 -04:00
|
|
|
stable.test('::ng-deep, ::deep, ::v-deep pseudo elements are left alone', () => {
|
2023-04-04 12:46:20 -04:00
|
|
|
let config = {
|
2024-01-05 14:39:34 -05:00
|
|
|
darkMode: 'selector',
|
2023-04-04 12:46:20 -04:00
|
|
|
content: [
|
|
|
|
|
{
|
|
|
|
|
raw: html` <div class="foo bar"></div> `,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
|
|
|
|
::ng-deep .foo .bar {
|
|
|
|
|
@apply font-bold;
|
|
|
|
|
}
|
2023-04-07 10:45:47 -04:00
|
|
|
::v-deep .foo .bar {
|
|
|
|
|
@apply font-bold;
|
|
|
|
|
}
|
|
|
|
|
::deep .foo .bar {
|
|
|
|
|
@apply font-bold;
|
|
|
|
|
}
|
2023-04-04 12:46:20 -04:00
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return run(input, config).then((result) => {
|
|
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
::ng-deep .foo .bar {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
2023-04-07 10:45:47 -04:00
|
|
|
::v-deep .foo .bar {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
::deep .foo .bar {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
2023-04-04 12:46:20 -04:00
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 1. `::ng-deep` is deprecated
|
2023-04-07 10:45:47 -04:00
|
|
|
// 2. `::deep` and `::v-deep` are non-standard
|
|
|
|
|
// 3. They all use invalid selector syntax that Lightning CSS does not support
|
2023-04-04 12:46:20 -04:00
|
|
|
// It may be enough for Oxide to not support it at all
|
2023-04-07 10:45:47 -04:00
|
|
|
oxide.test.todo('::ng-deep, ::deep, ::v-deep pseudo elements are left alone')
|
2023-09-29 16:04:32 -04:00
|
|
|
|
|
|
|
|
test('should not break replacing important selector when the same as the parent selector (pseudo)', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
important: ':root',
|
|
|
|
|
content: [],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@layer components {
|
|
|
|
|
:root {
|
|
|
|
|
@apply flex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
let result = await run(input, config)
|
|
|
|
|
|
|
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
:root {
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('should not break replacing important selector when the same as the parent selector (class)', async () => {
|
|
|
|
|
let config = {
|
|
|
|
|
important: '.foo',
|
|
|
|
|
content: [
|
|
|
|
|
{
|
|
|
|
|
raw: html` <div class="foo"></div> `,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input = css`
|
|
|
|
|
@tailwind components;
|
|
|
|
|
@layer components {
|
|
|
|
|
.foo {
|
|
|
|
|
@apply flex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
let result = await run(input, config)
|
|
|
|
|
|
|
|
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
|
|
|
.foo {
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
`)
|
|
|
|
|
})
|
2022-11-03 12:20:38 +01:00
|
|
|
})
|