tailwind2-4 Exercise 2: has, not, in and nth ============================================== Setup: a folder with Tailwind installed (npm install tailwindcss @tailwindcss/cli). Save the two files below, then build and open the page in Chrome: npx @tailwindcss/cli -i ex2.css -o ex2.out.css To read a value by hand, use the console, for example: getComputedStyle(document.querySelector('#s_card')).color (The results below were measured with a scripted Chrome that used real mouse and click events; the same values appear when you hover and click yourself.) --- ex2.css --- @import "tailwindcss" source(none); @source "./ex2.html"; --- ex2.html ---
in
Results: E0. pointer never moved: in-hover span normal | body:hover false E1. pointer over an unrelated label: in-hover span RED | body:hover true | #inp:hover false C. unchecked label bg oklch(0.967 0.003 264.542) | disabled-label opacity 0.5 C. after clicking the checkbox, label bg green-500 D. li1 bg rgba(0, 0, 0, 0) | underline underline D. li2 (.skip) bg blue-500 | underline none F. li1 bg rgba(0, 0, 0, 0) underline underline italic normal weight 700 F. li2 bg rgba(0, 0, 0, 0) underline none italic normal weight 400 F. li3 bg red-500 underline underline italic normal weight 700 F. li4 bg rgba(0, 0, 0, 0) underline none italic italic weight 400 F. li5 bg rgba(0, 0, 0, 0) underline underline italic normal weight 700 WHY THIS WORKS AS AN ANSWER --------------------------- has-checked:bg-green-500 on the label compiles to :has(:checked): the label (the parent) changes when a descendant is checked, which CSS could not do before :has(). The disabled checkbox gives has-[input:disabled]:opacity-50 its 0.5 from the start. not-first:bg-blue-500 is :not(:first-child), so li1 has no background and li2 does; not-[.skip]:underline is :not(.skip), so the item with the class "skip" is the one that loses its underline. The nth results follow the selectors: odd:font-bold and nth-[2n+1]:underline both match li1, li3 and li5; nth-3:bg-red-500 matches only li3; nth-last-2:italic matches the second from the end, li4. The in-hover test shows a trap: in-hover:text-red-500 is :where(:hover) .class, meaning "any ancestor that is hovered", and the ancestors include body and html. With the pointer never moved the span is normal; with the pointer over an unrelated label it is red, and the output shows body:hover true while the span's own parent (#inp) was not hovered at all. In-* variants are safe when the ancestor is a specific thing (in-data-[state=open], in-[.dark]); in-hover and in-focus match far more than you would guess.