-
-
Notifications
You must be signed in to change notification settings - Fork 76
/
no-custom-classname.js
1492 lines (1484 loc) · 51.6 KB
/
no-custom-classname.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* @fileoverview Detect classnames which do not belong to Tailwind CSS
* @author François Massart
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
var rule = require("../../../lib/rules/no-custom-classname");
var RuleTester = require("eslint").RuleTester;
//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------
var parserOptions = {
ecmaVersion: 2019,
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
};
const skipClassAttributeOptions = [
{
skipClassAttribute: true,
config: {
theme: {},
plugins: [],
},
},
];
var generateErrors = (classnames) => {
const errors = [];
if (typeof classnames === "string") {
classnames = classnames.split(" ");
}
classnames.map((classname) => {
errors.push({
messageId: "customClassnameDetected",
data: {
classname: classname,
},
});
});
return errors;
};
var ruleTester = new RuleTester({ parserOptions });
ruleTester.run("no-custom-classname", rule, {
valid: [
{
code: `<div className="ns-dark">Custom dark class</div>`,
options: [
{
config: { darkMode: ["class", ".ns-dark"] },
},
],
},
{
code: `<div class="group peer">Hover, Focus, & Other States</div>`,
},
{
code: `<div className="dark">dark should be allowed in class mode</div>`,
options: [
{
config: { darkMode: "class" },
},
],
},
{
code: `<div className="[mask-type:luminance] hover:[mask-type:alpha] [--scroll-offset:56px] grid grid-cols-[1fr_500px_2fr]">dark should be allowed in class mode</div>`,
},
{
code: `
<section>
<h1>Layout utilities</h1>
<div class="container">Container</div>
<div class="box-content lg:box-border">Box Sizing</div>
<div class="inline-block sm:block md:flex lg:table">Display</div>
<div class="float-none sm:float-left md:float-right">Floats</div>
<div class="clear-left sm:clear-right md:clear-both lg:clear-none">Clear</div>
<div class="isolate sm:isolation-auto">Isolation</div>
<div class="object-contain sm:object-cover md:object-fill lg:object-none dark:object-scale-down">Object Fit</div>
<div class="overflow-auto sm:overflow-scroll">Overflow</div>
<div class="overscroll-none md:overscroll-y-auto">Overscroll Behavior</div>
<div class="fixed sm:absolute">Position</div>
<div class="visible sm:invisible">Visibility</div>
</section>`,
},
{
code: `<div class="columns-2 hover:columns-3 lg:columns-[10rem]">Columns</div>`,
},
{
code: `<div class="break-after-auto md:break-after-avoid-page break-before-left break-inside-avoid-column">Break</div>`,
},
{
code: `<div class="box-decoration-clone sm:box-decoration-slice">Box Decoration Break</div>`,
},
{
code: `<div class="object-right object-[center_bottom]">Object Position</div>`,
},
{
code: `<div class="inset-x-0 top-2 -left-2 bottom-[3px] right-[-3px]">Top / Right / Bottom / Left</div>`,
},
{
code: `<div class="z-0 sm:z-1000 md:-z-1000 lg:z-[100] dark:-z-0 sm:dark:z-1000 md:dark:-z-1000 lg:dark:z-[100] xl:dark:-z-666">Z-Index</div>`,
options: [
{
config: {
theme: {
extend: {
zIndex: {
0: "0",
"-666": "-666",
1000: "1000",
},
},
},
},
},
],
},
{
code: `
<section>
<h1>Flexbox & Grid</h1>
<div class="basis-[14.2857143%] sm:basis-1">Flex Basis</div>
<div class="flex-row">Flex Direction</div>
<div class="flex-wrap">Flex Wrap</div>
<div class="flex-auto sm:flex-1 flex-[2_2_0%]">Flex</div>
<div class="grow sm:grow-0 md:grow-[2]">Flex Grow</div>
<div class="shrink sm:shrink-0 md:shrink-[2]">Flex Shrink</div>
<div class="order-1 sm:order-[13]">Order</div>
<div class="grid-cols-6 sm:grid-cols-[200px_minmax(900px,_1fr)_100px]"></div>
<div class="col-auto sm:col-span-1 md:col-[span_1_/_span_3]">Grid Column Start / End</div>
<div class="grid-rows-4 sm:grid-rows-[200px_minmax(900px,_1fr)_100px]">Grid Template Rows</div>
<div class="row-auto sm:row-span-6 md:row-span-full lg:row-[span_16_/_span_16]">Grid Row Start / End</div>
<div class="grid-flow-row-dense">Grid Auto Flow</div>
<div class="auto-cols-max sm:auto-cols-[minmax(0,_2fr)]">Grid Auto Columns</div>
<div class="auto-rows-max auto-rows-[minmax(0,_2fr)]">Grid Auto Rows</div>
<div class="gap-x-0 sm:gap-y-3 gap-[2.75rem]">Gap</div>
<div class="justify-end ">Justify Content</div>
<div class="justify-items-end ">Justify Items</div>
<div class="justify-self-start">Justify Self</div>
<div class="content-center">Align Content</div>
<div class="items-baseline">Align Items</div>
<div class="self-stretch">Align Self</div>
<div class="place-content-evenly">Place Content</div>
<div class="place-items-center">Place Items</div>
<div class="place-self-center">Place Self</div>
</section>`,
},
{
code: `
<section>
<h1>Spacing</h1>
<div class="p-1 sm:pr-0 md:px-[5px]">Padding</div>
<div class="m-1 sm:mr-0 md:mx-[5px] lg:-ml-1">Margin</div>
<div class="space-x-0 sm:space-y-1 md:space-x-reverse lg:space-y-reverse xl:space-y-[5px] dark:-space-x-1 sm:dark:-space-y-1 xl:dark:space-y-[-5px]">Space Between</div>
</section>`,
},
{
code: `
<section>
<h1>Sizing</h1>
<div class="w-0 sm:w-px md:w-1/2 lg:w-max xl:w-[32rem]">Width</div>
<div class="min-w-0 sm:min-w-full md:min-w-min lg:min-w-max xl:min-w-[50%]">Min-Width</div>
<div class="max-w-0 sm:max-w-none md:max-w-2xl lg:max-w-screen-xl xl:max-w-[50%]">Max-Width</div>
<div class="h-0 sm:h-px md:h-1/2 lg:h-max xl:h-[32rem]">Height</div>
<div class="min-h-0 sm:min-h-full md:min-h-min lg:min-h-max xl:min-h-[50%]">Min-Height</div>
<div class="max-h-0 sm:max-h-2 md:max-h-full lg:max-h-screen xl:max-h-[32rem]">Max-Height</div>
<div class=""></div>
</section>`,
},
{
code: `
<section>
<h1>Typography</h1>
<div class="font-sans sm:font-serif md:font-mono lg:font-['Open_Sans']">Font Family</div>
<div class="text-xs sm:text-base md:text-2xl lg:text-[14px]">Font Size</div>
<div class="antialiased sm:subpixel-antialiased">Font Smoothing</div>
<div class="italic sm:not-italic">Font Style</div>
<div class="font-thin sm:font-extralight md:font-light lg:font-normal xl:font-medium dark:font-semibold sm:dark:font-bold md:dark:font-extrabold lg:dark:font-black xl:dark:font-[1100]">Font Weight</div>
<div class="ordinal slashed-zero tabular-nums sm:normal-nums md:lining-nums lg:oldstyle-nums xl:proportional-nums 2xl:tabular-nums">Font Variant Numeric</div>
<div class="tracking-tighter sm:tracking-tight md:tracking-normal lg:tracking-wide xl:tracking-wider 2xl:tracking-widest dark:tracking-[.25em] sm:dark:-tracking-wide md:dark:-tracking-normal lg:dark:tracking-[-.25em]">Letter Spacing</div>
<div class="leading-3 sm:leading-none md:leading-tight lg:leading-snug xl:leading-normal 2xl:leading-relaxed dark:leading-loose sm:dark:leading-[3rem]">Line Height</div>
<div class="list-none sm:list-disc md:list-decimal lg:list-[upper-roman]">List Style Type</div>
<div class="list-inside sm:list-outside">List Style Position</div>
<div class="text-left sm:text-center md:text-right lg:text-justify">Text Alignment</div>
<div class="text-inherit sm:text-current md:text-transparent lg:text-black xl:text-white 2xl:text-slate-200 dark:text-[#50d71e] sm:dark:text-[color:var(--yolo)] first:text-black/50 last:text-[#F005] even:text-[#F0F5F6B8] odd:text-[rgba(100,10,140,.5)]">Text Color</div>
<div class="underline sm:overline md:line-through lg:no-underline">Text Decoration</div>
<div class="decoration-inherit sm:decoration-current md:decoration-transparent lg:text-black xl:decoration-white 2xl:decoration-slate-200 dark:decoration-[#50d71e] sm:dark:decoration-[color:var(--yolo)] first:decoration-black/50 last:decoration-[#F005] even:decoration-[#F0F5F6B8] odd:decoration-[rgba(100,10,140,.5)]">Text Decoration Color</div>
<div class="decoration-solid sm:decoration-double md:decoration-dotted lg:decoration-dashed xl:decoration-wavy">Text Decoration Style</div>
<div class="decoration-auto sm:decoration-from-font md:decoration-0 lg:decoration-8 xl:decoration-[3px] 2xl:decoration-[length:var(--yolo)]">Text Decoration Thickness</div>
<div class="underline-offset-auto sm:underline-offset-0 md:underline-offset-8 underline-offset-[3px]">Text Underline Offset</div>
<div class="uppercase sm:lowercase md:capitalize lg:normal-case">Text Transform</div>
<div class="truncate sm:text-ellipsis md:text-clip">Text Overflow</div>
<div class="indent-0 sm:indent-3 md:indent-[50%] lg:-indent-3 xl:indent-[-10px]">Text Indent</div>
<div class="align-baseline sm:align-top md:align-middle lg:align-bottom xl:align-text-top dark:align-text-bottom sm:dark:align-sub md:dark:align-super">Vertical Alignment</div>
<div class="whitespace-normal sm:whitespace-nowrap md:whitespace-pre lg:whitespace-pre-line xl:whitespace-pre-wrap">Whitespace</div>
<div class="break-normal sm:break-words md:break-all">Word Break</div>
<div class="content-none after:content-['_↗'] before:content-[attr(before)] sm:before:content-['Hello_World'] md:before:content-['Hello\_World'] lg:before:content-[url('/icons/link.svg')]">Content</div>
</section>`,
},
{
code: `
<section>
<h1>Backgrounds</h1>
<div class="bg-fixed sm:bg-local md:bg-scroll">Background Attachment</div>
<div class="bg-clip-border sm:bg-clip-padding md:bg-clip-content lg:bg-clip-text">Background Clip</div>
<div class="bg-inherit sm:bg-current md:bg-black lg:bg-lime-900 xl:bg-[#50d71e] 2xl:bg-[color:var(--some)]">Background Color</div>
<div class="bg-origin-border sm:bg-origin-padding md:bg-origin-content">Background Origin</div>
<div class="bg-bottom sm:bg-center md:bg-left lg:bg-left-bottom xl:bg-left-top 2xl:bg-right dark:bg-right-bottom sm:dark:bg-right-top md:dark:bg-top lg:dark:bg-[center_top_1rem]">Background Position</div>
<div class="bg-repeat sm:bg-no-repeat md:bg-repeat-x lg:bg-repeat-y xl:bg-repeat-round 2xl:bg-repeat-space">Background Repeat</div>
<div class="bg-auto sm:bg-cover md:bg-contain lg:bg-[length:200px_100px]">Background Size</div>
<div class="bg-none sm:bg-gradient-to-tr md:bg-[url('/img/hero-pattern.svg')]">Background Image</div>
<div class="from-inherit sm:from-current md:from-transparent lg:from-black xl:from-[#243c5a] 2xl:from-[#243c5a/50] first:from-black/50 last:from-[#F005] even:from-[#F0F5F6B8] odd:from-[rgba(100,10,140,.5)]">Gradient Color Stops</div>
</section>`,
},
{
code: `
<section>
<h1>Borders</h1>
<div class="rounded-none sm:rounded-xl md:rounded-t-md lg:rounded-br-3xl xl:rounded-[12px]">Border Radius</div>
<div class="border sm:border-0 md:border-x-2 lg:border-y-8 xl:border-r 2xl:border-t-[3px]">Border Width</div>
<div class="border-transparent sm:border-rose-500 md:border-t-indigo-500/75 lg:border-x-indigo-500 xl:border-[#243c5a] first:border-black/50 last:border-[#F005] even:border-[#F0F5F6B8] odd:border-[rgba(100,10,140,.5)]">Border Color</div>
<div class="border-solid sm:border-dashed md:border-dotted lg:border-double xl:border-hidden 2xl:border-none">Border Style</div>
<div class="divide-y-4 sm:divide-x-8 md:divide-y lg:divide-y-reverse xl:divide-x-reverse 2xl:divide-x-[3px]">Divide Width</div>
<div class="divide-gray-400/25 sm:divide-amber-400 md:divide-[#243c5a] first:divide-black/50 last:divide-[#F005] even:divide-[#F0F5F6B8] odd:divide-[rgba(100,10,140,.5)]">Divide Color</div>
<div class="divide-solid sm:divide-dashed md:divide-dotted lg:divide-double xl:divide-none">Divide Style</div>
<div class="outline-0 sm:outline-4 md:outline-[5px]">Outline Width</div>
<div class="outline-inherit sm:outline-current md:outline-black lg:outline-lime-900 xl:outline-[#50d71e] 2xl:outline-[color:var(--some)] first:outline-black/50 last:outline-[#F005] even:outline-[#F0F5F6B8] odd:outline-[rgba(100,10,140,.5)]">Outline Color</div>
<div class="outline-none sm:outline md:outline-dashed lg:outline-dotted xl:outline-double 2xl:outline-hidden">Outline Style</div>
<div class="outline-offset-4 sm:outline-offset-[3px]">Outline Offset</div>
<div class="ring-0 sm:ring-4 md:ring-inset lg:ring-[10px]">Ring Width</div>
<div class="ring-inherit sm:ring-current md:ring-black lg:ring-lime-900 xl:ring-[#50d71e] 2xl:ring-[color:var(--some)] first:ring-black/50 last:ring-[#F005] even:ring-[#F0F5F6B8] odd:ring-[rgba(100,10,140,.5)]">Ring Color</div>
<div class="ring-offset-4 sm:ring-offset-[3px]">Ring Offset Width</div>
<div class="ring-offset-inherit sm:ring-offset-current md:ring-offset-black lg:ring-offset-lime-900 xl:ring-offset-[#50d71e] 2xl:ring-offset-[color:var(--some)] first:ring-offset-black/50 last:ring-offset-[#F005] even:ring-offset-[#F0F5F6B8] odd:ring-offset-[rgba(100,10,140,.5)]">Ring Offset Color</div>
</section>`,
},
{
code: `
<section>
<h1>Effects</h1>
<div class="shadow sm:shadow-lg md:shadow-inner lg:shadow-none xl:shadow-[0_35px_60px_-15px_rgba(0,0,0,0.3)]">Box Shadow</div>
<div class="shadow-inherit sm:shadow-current md:shadow-black lg:shadow-lime-900 xl:shadow-[#50d71e] 2xl:shadow-[color:var(--some)] first:shadow-black/50 last:shadow-[#F005] even:shadow-[#F0F5F6B8] odd:shadow-[rgba(100,10,140,.5)]">Box Shadow Color</div>
<div class="opacity-0 sm:opacity-50 md:opacity-[.67]">Opacity</div>
<div class="mix-blend-normal sm:mix-blend-multiply md:mix-blend-screen lg:mix-blend-overlay xl:mix-blend-darken 2xl:mix-blend-lighten dark:mix-blend-color-dodge sm:dark:mix-blend-color-burn md:dark:mix-blend-hard-light lg:dark:mix-blend-soft-light xl:dark:mix-blend-difference 2xl:dark:mix-blend-exclusion hover:mix-blend-hue first:mix-blend-saturation last:mix-blend-color focus:mix-blend-luminosity">Mix Blend Mode</div>
<div class="bg-blend-normal sm:bg-blend-multiply md:bg-blend-screen lg:bg-blend-overlay xl:bg-blend-darken 2xl:bg-blend-lighten dark:bg-blend-color-dodge focus:bg-blend-color-burn link:bg-blend-hard-light visited:bg-blend-soft-light hover:bg-blend-difference active:bg-blend-exclusion first:bg-blend-hue last:bg-blend-saturation odd:bg-blend-color even:bg-blend-luminosity">Background Blend Mode</div>
</section>`,
},
{
code: `
<section>
<h1>Filters</h1>
<div class="blur-none sm:blur md:blur-lg lg:blur-[2px]">Blur</div>
<div class="brightness-0 sm:brightness-100 md:brightness-[1.75]">Brightness</div>
<div class="contrast-0 sm:contrast-100 md:contrast-[.75]">Contrast</div>
<div class="drop-shadow sm:drop-shadow-none md:drop-shadow-xl lg:drop-shadow-[0_35px_35px_rgba(0,0,0,0.25)]">Drop Shadow</div>
<div class="grayscale sm:grayscale-0 md:grayscale-[50%]">Grayscale</div>
<div class="hue-rotate-0 sm:hue-rotate-90 md:-hue-rotate-15 lg:hue-rotate-[270deg] xl:hue-rotate-[-7deg] 2xl:hue-rotate-[var(--yolo)]">Hue Rotate</div>
<div class="invert sm:invert-0 md:invert-[.25]">Invert</div>
<div class="saturate-0 sm:saturate-150 md:saturate-[.25]">Saturate</div>
<div class="sepia-0 sm:sepia md:sepia-[.25]">Sepia</div>
<div class="backdrop-blur sm:backdrop-blur-lg md:backdrop-blur-[2px]">Backdrop Blur</div>
<div class="backdrop-brightness-50 sm:backdrop-brightness-200 md:backdrop-brightness-[1.75]">Backdrop Brightness</div>
<div class="backdrop-contrast-100 sm:backdrop-contrast-0 md:backdrop-contrast-[.25]">Backdrop Contrast</div>
<div class="backdrop-grayscale-0 sm:backdrop-grayscale md:backdrop-grayscale-[.5]">Backdrop Grayscale</div>
<div class="-backdrop-hue-rotate-15 sm:backdrop-hue-rotate-15 md:backdrop-hue-rotate-[15deg]">Backdrop Hue Rotate</div>
<div class="backdrop-invert-0 sm:backdrop-invert md:backdrop-invert-[.25]">Backdrop Invert</div>
<div class="backdrop-opacity-50 sm:backdrop-opacity-25 md:backdrop-opacity-[.15]">Backdrop Opacity</div>
<div class="backdrop-saturate-150 sm:backdrop-saturate-50 md:backdrop-saturate-[.25]">Backdrop Saturate</div>
<div class="backdrop-sepia sm:backdrop-sepia-0 md:backdrop-sepia-[.25]">Backdrop Sepia</div>
</section>`,
},
{
code: `
<section>
<h1>Tables</h1>
<div class="border-collapse md:border-separate">Border Collapse</div>
<div class="table-auto sm:table-fixed">Table Layout</div>
</section>`,
},
{
code: `
<section>
<h1>Transitions & Animation</h1>
<div class="transition-none sm:transition-all md:transition lg:transition-colors xl:transition-opacity 2xl:transition-shadow dark:transition-transform first:transition-[height]">Transition Property</div>
<div class="duration-100 sm:duration-200 md:duration-[2000ms]">Transition Duration</div>
<div class="ease-linear sm:ease-in md:ease-out lg:ease-in-out xl:ease-[cubic-bezier(0.95,0.05,0.795,0.035)]">Transition Timing Function</div>
<div class="delay-75 sm:delay-500 md:delay-[2000ms]">Transition Delay</div>
<div class="animate-none sm:animate-spin md:animate-ping lg:animate-pulse xl:animate-bounce 2xl:animate-[wiggle_1s_ease-in-out_infinite]">Animation</div>
</section>`,
},
{
code: `
<section>
<h1>Transforms</h1>
<div class="scale-x-50 sm:scale-y-75 md:scale-0 lg:scale-[1.7] transform-gpu">Scale</div>
<div class="rotate-45 sm:-rotate-90 md:rotate-[17deg]">Rotate</div>
<div class="translate-x-2.5 sm:translate-y-3 md:-translate-y-10 lg:translate-y-[17rem]">Translate</div>
<div class="skew-y-3 skew-x-6 sm:-skew-y-6 md:skew-y-[17deg] lg:skew-y-[-45deg]">Skew</div>
<div class="origin-center sm:origin-top md:origin-top-right lg:origin-right xl:origin-bottom-right dark:origin-bottom first:origin-bottom-left last:origin-left odd:origin-top-left even:origin-[33%_75%]">Transform Origin</div>
</section>`,
},
{
code: `
<section>
<h1>Interactivity</h1>
<div class="accent-emerald-500/25 sm:accent-violet-300 2xl:accent-[#50d71e] first:accent-black/50 last:accent-[#F005] even:accent-[#F0F5F6B8] odd:accent-[rgba(100,10,140,.5)]">Accent Color</div>
<div class="appearance-none">Appearance</div>
<div class="cursor-auto sm:cursor-default md:cursor-pointer lg:cursor-wait xl:cursor-text 2xl:cursor-move dark:cursor-help sm:dark:cursor-not-allowed md:dark:cursor-none lg:dark:cursor-context-menu xl:dark:cursor-progress 2xl:dark:cursor-cell dark:focus:cursor-crosshair dark:active:cursor-vertical-text dark:valid:cursor-alias first:cursor-copy last:cursor-no-drop even:cursor-grab odd:cursor-grabbing empty:cursor-all-scroll disabled:cursor-col-resize active:cursor-row-resize visited:cursor-n-resize hover:cursor-e-resize focus:cursor-s-resize link:cursor-w-resize only:cursor-ne-resize focus-visible:cursor-nw-resize disabled:cursor-se-resize checked:cursor-sw-resize required:cursor-ew-resize invalid:cursor-ns-resize valid:cursor-nesw-resize in-range:cursor-nwse-resize out-of-range:cursor-zoom-in open:cursor-zoom-out open:first:cursor-[url(hand.cur),_pointer]">Cursor</div>
<div class="caret-emerald-500/25 sm:caret-violet-300 2xl:caret-[#50d71e] first:caret-black/50 last:caret-[#F005] even:caret-[#F0F5F6B8] odd:caret-[rgba(100,10,140,.5)]">Caret Color</div>
<div class="pointer-events-none sm:pointer-events-auto">Pointer Events</div>
<div class="resize-none sm:resize-y md:resize-x lg:resize">Resize</div>
<div class="scroll-auto sm:scroll-smooth">Scroll Behavior</div>
<div class="scroll-m-0 sm:scroll-mx-px md:scroll-my-0.5 lg:scroll-mb-1 xl:-scroll-mr-1 2xl:scroll-m-[24rem]">Scroll Margin</div>
<div class="scroll-p-0 sm:scroll-px-px md:scroll-py-0.5 lg:scroll-pb-1 xl:scroll-pr-1 2xl:scroll-p-[24rem]">Scroll Padding</div>
<div class="snap-start sm:snap-end md:snap-center lg:snap-align-none">Scroll Snap Align</div>
<div class="snap-normal sm:snap-always">Scroll Snap Stop</div>
<div class="snap-none sm:snap-x md:snap-y lg:snap-both xl:snap-mandatory 2xl:snap-proximity">Scroll Snap Type</div>
<div class="touch-auto sm:touch-none md:touch-pan-x lg:touch-pan-left xl:touch-pan-right 2xl:touch-pan-y dark::touch-pan-up first:touch-pan-down last:touch-pinch-zoom focus:touch-manipulation">Touch Action</div>
<div class="select-none sm:select-text md:select-all lg:select-auto">User Select</div>
<div class="will-change-auto sm:will-change-scroll md:will-change-contents lg:will-change-transform">Will Change</div>
</section>`,
},
{
code: `
<section>
<h1>SVG</h1>
<div class="fill-emerald-500 sm:fill-violet-300 2xl:fill-[#50d71e] first:fill-black/50 last:fill-[#F005] even:fill-[#F0F5F6B8] odd:fill-[rgba(100,10,140,.5)]">Fill</div>
<div class="stroke-emerald-500 sm:stroke-violet-300 2xl:stroke-[#50d71e] first:stroke-black/50 last:stroke-[#F005] even:stroke-[#F0F5F6B8] odd:stroke-[rgba(100,10,140,.5)]">Stroke</div>
<div class="stroke-0 sm:stroke-1 md:stroke-2 lg:stroke-[2px]">Stroke Width</div>
</section>`,
},
{
code: `
<section>
<h1>Accessibility</h1>
<div class="sr-only sm:not-sr-only">Screen Readers</div>
</section>`,
},
{
code: `
<section>
<h1>Official Plugins</h1>
<div class=""></div>
</section>`,
},
{
code: `<template><div class="container box-content lg:box-border max-h-24 self-end">Only Tailwind CSS classnames</div></template>`,
filename: "test.vue",
parser: require.resolve("vue-eslint-parser"),
},
{
code: `
ctl(\`
sm:w-6
w-8
container
w-12
flex
lg:w-4
\`);`,
},
{
code: `<div class="tw-container tw-box-content lg_tw-box-border">Only Tailwind CSS classnames</div>`,
options: [
{
config: { prefix: "tw-", separator: "_" },
},
],
},
{
code: `<div class="!arbitrary-inset-[123px]">Allow arbitrary value support + Built-in important modifier</div>`,
options: [
{
config: {
prefix: "arbitrary-",
},
},
],
},
{
code: `<div class="dark:focus:hover:bg-black md:dark:disabled:focus:hover:bg-gray-400">Stackable variants</div>`,
},
{
code: `<div class="bg-custom-color">Using dash in custom color name</div>`,
options: [
{
config: {
theme: {
colors: {
"custom-color": "#B4D4AA",
},
},
},
},
],
},
{
code: `<div className={clsx(\`flex w-full\`)}>clsx</div>`,
options: [
{
callees: ["clsx"],
},
],
},
{
code: `<div className="flex skin-summer custom-2">whitelisted</div>`,
options: [
{
whitelist: ["skin\\-(summer|xmas)", "custom\\-[1-3]"],
},
],
},
{
code: `<div className="text-foo border-bar">defined in textColor</div>`,
options: [
{
config: { theme: { textColor: { foo: "#123456" }, borderColor: { bar: "#654321" } } },
},
],
},
{
code: `
<div class="group border-indigo-500 hover:bg-white hover:shadow-lg hover:border-transparent">
<p class="text-indigo-600 group-hover:text-gray-900">New Project</p>
<p class="text-indigo-500 group-hover:text-gray-500">Create a new project from a variety of starting templates.</p>
</div>`,
},
{
code: `
<div class="some base white-listed classnames">
from css file
</div>`,
options: [
{
cssFiles: ["./tests/**/*.css"],
},
],
},
{
code: `
myTag\`
sm:w-6
w-8
container
w-12
flex
lg:w-4
\`;`,
options: [{ tags: ["myTag"] }],
},
{
code: `
<div class="flex flex-row-reverse space-x-4 space-x-reverse">
<div>1</div>
<div>2</div>
<div>3</div>
</div>`,
},
{
code: `
<div class="text-red-700 border-2 border-current bg-current p-10">
<input class="bg-gray-500 text-white placeholder:text-black" placeholder="placeholder color" />
<div class="text-yellow-400">
<div class="divide-current divide-y-4">
<button class="ring-2 p-2 focus:ring-current ring-offset-current ring-offset-2 outline-none">button with ringColor</button>
<div>2</div>
<div class="text-current">3</div>
</div>
</div>
<svg class="stroke-current text-yellow-400 stroke-2">
<circle cx="50" cy="50" r="20" />
</svg>
<svg class="fill-current text-yellow-400 stroke-0">
<circle cx="50" cy="50" r="20" />
</svg>
<div class="bg-gradient-to-r from-red-500 to-current text-green-300 h-20 w-full"></div>
<div class="bg-gradient-to-l from-current to-black text-green-300 h-20 w-full"></div>
</div>
`,
},
{
code: `
<div class="bg-red-600 p-10">
<p class="text-yellow-400 border-2 border-green-600 border-t-current p-2">border-t-current</p>
</div>
`,
},
{
code: `
<div class="aspect-none">
aspect-none is a valid classname
</div>`,
},
{
code: `
<div class="font-mono">
font-mono is a valid classname
</div>`,
},
{
code: `
<div className={'stroke-sky-500/[.1]'}>Arbitrary alpha suffix</div>`,
},
{
code: `
<div className={'!hidden sm:!flex lg:!block 2xl:!block'}>Important modifier</div>`,
},
{
code: `
<button
type="button"
className={classnames(
"p-2 font-medium",
boolVal ? false : "text-black"
)}
/>`,
},
{
code: `
<button
type="button"
className={classnames(
["p-2 font-medium"],
[{"text-black": boolVal}]
)}
/>`,
},
{
code: `
<div className="-mt-4">Negative value with custom config</div>`,
options: [
{
config: {
theme: {
spacing: {
4: "calc(4 * .25rem)",
},
},
},
},
],
},
{
code: `
<div className="transform-none">Disabling transform</div>`,
},
{
code: `
<div className="p/r[e].f!-x_flex">Nasty prefix</div>`,
options: [
{
config: {
prefix: "p/r[e].f!-x_",
},
},
],
},
{
code: `
<div className="text-9xl text-ffffff/[24%] bg-000000">Issue #101</div>`,
options: [
{
config: {
theme: {
colors: {
"000000": "#000000",
ffffff: "#ffffff",
},
},
plugins: [],
},
},
],
},
{
code: `
<div className="grid gap-x-4 grid-cols-1fr/minmax(0/360)/1fr">Issue #100</div>`,
options: [
{
config: {
theme: {
extend: {
gridTemplateColumns: {
"1fr/minmax(0/360)/1fr": "1fr minmax(0, calc(360 * .25rem)) 1fr",
},
},
},
plugins: [],
},
},
],
},
{
code: `
<div class="transform-none">Using</div>
<div class="flex flex-col">HTML</div>`,
parser: require.resolve("@angular-eslint/template-parser"),
},
{
code: `
<div class="p/r[e].f!-x_flex">Using HTML</div>
<div class="p/r[e].f!-x_block">With nasty prefix</div>`,
options: [
{
config: {
prefix: "p/r[e].f!-x_",
},
},
],
parser: require.resolve("@angular-eslint/template-parser"),
},
{
code: `
<div class="decoration-clone decoration-slice flex-grow flex-shrink overflow-clip overflow-ellipsis bg-opacity-50 border-opacity-100 transform scale-50">Deprecated classnames yet still supported for now</div>`,
},
{
code: `
<div class="-ml-[1px] mr-[-1px]">Negative arbitrary value</div>`,
},
{
code: `
<div className={ctl(\`
leading-loose
prose
md:prose-xl
lg:prose-lg
lg:prose-h1:text-lg
lg:prose-h1:leading-[2.75rem]
lg:prose-h2:text-sm
lg:prose-h2:leading-[2.125rem]
lg:prose-h3:text-xl
lg:prose-h3:leading-[1.8125rem]
lg:prose-blockquote:py-60
lg:prose-blockquote:pr-[5rem]
lg:prose-blockquote:pl-[6rem]
lg:prose-p:text-xl
lg:prose-p:leading-loose
dark:prose-headings:text-red-100
dark:prose-hr:border-black
dark:prose-code:text-pink-100
dark:prose-blockquote:text-orange-100
dark:prose-a:bg-black
dark:prose-a:text-black
dark:prose-a:visited:bg-black
dark:prose-a:visited:text-teal-200
dark:prose-a:hover:bg-black
dark:prose-a:hover:text-black
dark:prose-a:focus:outline-white
dark:prose-thead:bg-purple-500
dark:prose-thead:text-black
dark:prose-tr:border-b-purple-500
dark:prose-pre:bg-[#1f2227]
dark:prose-pre:text-[#639bee]
dark:prose-li:before:text-green-50
\`)}>
https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/97
</div>`,
options: [
{
config: {
plugins: [require("@tailwindcss/typography")],
},
},
],
},
{
code: `
<div class="bg-[#ccc]/[75%] border-t-[#000]/[5]">Issue #130</div>`,
},
{
code: `
<div class="border-spacing-2">
Issue #148
https://github.com/tailwindlabs/tailwindcss/releases/tag/v3.1.0
</div>`,
},
{
code: `
<div class="prose prose-red prose-xl dark:prose-invert prose-img:rounded-xl prose-headings:underline prose-a:text-blue-600">
Support for plugins
<p class="not-prose">Not prose</p>
<div class="aspect-w-16 aspect-h-9">
<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p class="line-clamp-3">Line clamp</p>
</div>`,
options: [
{
config: {
plugins: [
require("@tailwindcss/typography"),
require("@tailwindcss/aspect-ratio"),
require("@tailwindcss/line-clamp"),
],
},
},
],
},
{
code: `
<div>
<h1 className="text-red-500">Hello, world!</h1>
<button className="btn">Hello</button>
</div>`,
options: [
{
config: {
plugins: [require("daisyui")],
},
},
],
},
{
code: `
<div class="text-example-1 text-category-example-1">
https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/145
</div>`,
options: [
{
config: {
theme: {
colors: {
transparent: "transparent",
current: "currentColor",
white: "#FFFFFF",
black: "#000000",
example: {
1: "#F0f025",
},
category: {
example: {
1: "#F0F025",
},
},
},
},
},
},
],
},
{
code: `
<div>
<div class="bg-fnprimary">PRIMARY using a function</div>
<div class="bg-fnsecondary">SECONDARY using a function</div>
<p>See https://github.com/adamwathan/tailwind-css-variable-text-opacity-demo</p>
</div>`,
options: [
{
config: {
theme: {
colors: {
fnprimary: ({ opacityVariable, opacityValue }) => {
if (opacityValue !== undefined) {
return `rgba(var(--color-primary), ${opacityValue})`;
}
if (opacityVariable !== undefined) {
return `rgba(var(--color-primary), var(${opacityVariable}, 1))`;
}
return `rgb(var(--color-primary))`;
},
fnsecondary: ({ opacityVariable, opacityValue }) => {
if (opacityValue !== undefined) {
return `rgba(var(--color-secondary), ${opacityValue})`;
}
if (opacityVariable !== undefined) {
return `rgba(var(--color-secondary), var(${opacityVariable}, 1))`;
}
return `rgb(var(--color-secondary))`;
},
},
},
},
},
],
},
{
code: `
<div class="grid-flow-dense mix-blend-plus-lighter border-separate border-spacing-4">
grid-flow-dense, mix-blend-plus-lighter
</div>`,
},
{
code: `
<div class="
border-spacing-y-0
border-spacing-px
border-spacing-x-1
border-spacing-y-2.5
border-spacing-96"
>
border-spacing
</div>`,
},
{
code: `<div class>No errors while typing</div>`,
},
{
code: `<div class="supports-[transform-origin:5%_5%]:grid supports-[display:grid]:grid">https://github.com/tailwindlabs/tailwindcss/pull/9453</div>`,
},
{
code: `<div class="px-5 max-sm:px-2 sm:px-4">https://github.com/tailwindlabs/tailwindcss/pull/9558</div>`,
},
{
code: `<div aria-checked="true" class="bg-gray-600 aria-checked:bg-blue-600">https://github.com/tailwindlabs/tailwindcss/pull/9557</div>`,
},
{
code: `<div className="custom-but-allowed">negated whitelist will only check classes starting with "text-"</div>`,
options: [
{
whitelist: ["(?!text\\-).*"],
},
],
},
{
code: `
cva({
variants: {
variant: {
primary: ["sm:w-6 w-8 container w-12 flex lg:w-4"],
primary: ["sm:w-6 w-8 container w-12 flex lg:w-4"],
},
},
});
`,
options: [
{
callees: ["cva"],
},
],
},
{
code: `
cva({
primary: ["sm:w-6 w-8 container w-12 flex lg:w-4"],
});
`,
options: [
{
callees: ["cva"],
},
],
},
{
code: `<div className="text-primary-500 text-xl">https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/197</div>`,
options: [
{
config: {
theme: {
extend: {
colors: {
primary: {
50: "#fdf2f8",
100: "#fce7f3",
200: "#fbcfe8",
300: "#f9a8d4",
400: "#f472b6",
500: "#ec4899",
600: "#db2777",
700: "#be185d",
800: "#9d174d",
900: "#831843",
},
},
},
},
},
},
],
},
{ code: `<div className="group/edit">Custom group name</div>` },
{
code: `<div className="tw-group/edit">Prefix and custom group name</div>`,
options: [
{
config: { prefix: "tw-" },
},
],
},
{ code: `<div className="group-hover/edit:hidden">Custom group name variant</div>` },
{
code: `<div className="group-hover/edit:tw-hidden">Prefix and custom group name variant</div>`,
options: [
{
config: { prefix: "tw-" },
},
],
},
{
code: `
<div class="group/nav123$#@%^&*_-">
<div class="group-hover/nav123$#@%^&*_-:h-0">group/nav123$#@%^&*_-</div>
</div>
`,
},
{
code: `
<div class="tw-group/nav123$#@%^&*_-">
<div class="group-hover/nav123$#@%^&*_-:tw-h-0">group/nav123$#@%^&*_-</div>
</div>
`,
options: [
{
config: { prefix: "tw-" },
},
],
},
{
code: `<div className="group-hover/edit:custom-class">Custom group name variant</div>`,
options: [
{
whitelist: ["custom-class"],
},
],
},
{ code: `<div className="peer/draft">Custom peer name</div>` },
{
code: `<div className="tw-peer/draft">Prefix and custom peer name</div>`,
options: [
{
config: { prefix: "tw-" },
},
],
},
{ code: `<div className="peer-checked/draft:hidden">Custom peer name variant</div>` },
{
code: `<div className="peer-checked/draft:tw-hidden">Prefix and custom peer name variant</div>`,
options: [
{
config: { prefix: "tw-" },
},
],
},
{
code: `<div className="peer-checked/draft:custom-class">Custom peer name variant</div>`,
options: [
{
whitelist: ["custom-class"],
},
],
},
{
code: `
// https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/193
const button = cva(["font-semibold", "border", "rounded"], {
variants: {
intent: {
primary: "bg-blue-500 text-white border-transparent hover:bg-blue-600",
secondary: [
"bg-white",
"text-gray-800",
"border-gray-400",
"hover:bg-gray-100",
],
},
size: {
small: ["text-sm", "py-1", "px-2"],
medium: ["text-base", "py-2", "px-4"],
},
},
compoundVariants: [
{
intent: "primary",
size: "medium",
class: "uppercase",
},
],
defaultVariants: {
intent: "primary",
size: "medium",
},
});
`,
options: [
{
callees: ["cva"],
},
],
},
{
code: `
const obj = { a: 12 };
<div className={{
...obj
}}>Spread inside classname object</div>
`,
},
{
code: `
<div>
<div className={'h-svh min-h-svh max-h-svh'}>Dynamic viewport units</div>
<div className={'h-lvh min-h-lvh max-h-lvh'}>Dynamic viewport units</div>