tailwind2-8 Exercise 1: Run the Upgrade Tool and Measure What Is Left ===================================================================== Setup: a folder "proj" with npm install tailwindcss@3 postcss autoprefixer @tailwindcss/typography@0.5 (I had 3.4.19 and typography 0.5.20). Add a .gitignore containing node_modules BEFORE the first git add, then git init, git add -A, git commit. Files: --- tailwind.config.js --- /** @type {import('tailwindcss').Config} */ module.exports = { darkMode: 'class', content: ['./index.html', './src/**/*.{html,js}'], safelist: ['bg-emerald-500','bg-pink-' + '500'], theme: { extend: { colors: { brand: { 500: '#4f46e5', 600: '#4338ca' }, surface: '#f1f5f9' }, fontFamily: { display: ['Georgia', 'serif'] }, screens: { '3xl': '1920px' }, borderRadius: { card: '0.75rem' }, }, }, plugins: [require('@tailwindcss/typography')], }; --- postcss.config.js --- module.exports = { plugins: { tailwindcss: {}, autoprefixer: {} } }; --- src/input.css --- @tailwind base; @tailwind components; @tailwind utilities; @layer base { h1 { @apply text-2xl font-bold; } } @layer components { .btn { @apply inline-flex items-center rounded-md bg-brand-500 px-4 py-2 text-sm font-medium text-white hover:bg-brand-600 focus:outline-none focus:ring-2; } .panel { background: theme('colors.surface'); border-radius: theme('borderRadius.card'); padding: theme(spacing.4); } } --- index.html ---

Migration demo

A small page using several things that changed in v4.

Important and ellipsis

safelisted + transform

Prose

Typography plugin text.

Build the baseline: npx tailwindcss -i src/input.css -o dist/out.css Baseline screenshot: chrome.exe --headless=new --disable-gpu --hide-scrollbars --user-data-dir= --window-size=700,700 --screenshot=v3.png file:///.../proj/index.html --- probe.mjs (prints computed styles; uses my own DevTools driver cdp.mjs, so in practice read the same values in DevTools) --- import { withPage } from '../c4/cdp.mjs'; const dir = process.argv[2]; await withPage(`file:///C:/lwdj/tw2/c8/${dir}/index.html`, async (p) => { const g = (s, q) => p.css(s, q); console.log('b3 border-color ', await g('#b3','border-top-color'), '| border-width', await g('#b3','border-top-width')); console.log('b3 border-radius ', await g('#b3','border-top-left-radius')); console.log('b3 box-shadow ', (await g('#b3','box-shadow')).slice(0,140)); console.log('b3 outline ', await g('#b3','outline-style'), await g('#b3','outline-width')); console.log('b3 flex-grow ', await g('#b3','flex-grow')); console.log('b2 background ', await g('#b2','background-color')); console.log('btn (first) bg ', await g('.btn','background-color')); console.log('panel bg / radius ', await g('.panel','background-color'), await g('.panel','border-top-left-radius')); console.log('h1 font-size / family', await g('h1','font-size'), (await g('h1','font-family')).slice(0,20)); console.log('space-x: btn1 margin-right / btn2 margin-left', await g('.btn','margin-right'), await g('#b2','margin-left')); console.log('ellipsis text-overflow', await g('p.text-brand-600','text-overflow'), '| !font-bold weight', await g('p.text-brand-600','font-weight')); console.log('gradient bg-image ', (await g('.h-8','background-image')).slice(0,60)); console.log('safelisted/transform ', await g('.rounded-card','background-color'), await g('.rounded-card','transform')); console.log('prose h2 font-size ', await g('.prose h2','font-size')); console.log('body bg ', await g('body','background-color')); }); --- pix.mjs (npm install pngjs pixelmatch) --- import fs from 'node:fs'; import { PNG } from 'pngjs'; import pixelmatch from 'pixelmatch'; const [a,b,out]=process.argv.slice(2); const A=PNG.sync.read(fs.readFileSync(a)),B=PNG.sync.read(fs.readFileSync(b)); const d=new PNG({width:A.width,height:A.height}); const n=pixelmatch(A.data,B.data,d.data,A.width,A.height,{threshold:0.1}); if(out) fs.writeFileSync(out,PNG.sync.write(d)); console.log(a,'vs',b,'differing pixels',n,'of',A.width*A.height); Copy the project to "up" (cp -r, including node_modules), then in "up": npx @tailwindcss/upgrade npm install @tailwindcss/cli@4 node node_modules/@tailwindcss/cli/dist/index.mjs -i src/input.css -o dist/out.css exit 0, no warnings Dirty-tree check (a second copy with one uncommitted edit): npx @tailwindcss/upgrade -> "Git directory is not clean. Please stash or commit your changes before migrating." "You may use the `--force` flag ..." exit code 1, nothing changed Upgrade output (25 s): linked tailwind.config.js to src/input.css; "could not be automatically migrated to the new CSS configuration format, so your CSS has been updated to load your existing configuration file"; migrated input.css, index.html, postcss.config.js; updated package tailwindcss; installed @tailwindcss/postcss. Changed files: index.html, package.json, package-lock.json, postcss.config.js, src/input.css. Template renames: shadow->shadow-sm, shadow-sm->shadow-xs, rounded->rounded-sm, ring->ring-3, outline-none->outline-hidden, flex-grow->grow, bg-gradient-to-r->bg-linear-to-r, blur-sm->blur-xs, overflow-ellipsis->text-ellipsis, !font-bold->font-bold! input.css: @import 'tailwindcss'; @config '../tailwind.config.js'; a compatibility @layer base block restoring the gray-200 border colour; .btn and .panel became @utility blocks; theme('colors.surface') became var(--color-surface). Results: node pix.mjs v3.png up1.png v3.png vs up1.png differing pixels 4279 of 490000 diff of the probe output, v3 against after-the-tool: b3 border-color: rgb(229,231,235) -> oklch(0.928 0.006 264.531) same colour, new notation b3 box-shadow: 3px ring rgba(59,130,246,0.5) -> 3px ring in oklch(0.279 0.041 260.031) (the text colour) RING CHANGED b3 outline: solid 2px -> none 3px outline-none became outline-hidden b2 background: rgba(0,0,0,0.5) -> rgb(0,0,0) bg-opacity-50 REMOVED panel bg/radius: rgb(241,245,249) 12px -> rgba(0,0,0,0) 0px var(--color-surface) / var(--radius-card) UNDEFINED space-x: btn2 margin-left 8px -> btn1 margin-right 8px same layout, different selector gradient / emerald: same colours, written in oklab/oklch ; transform matrix(1.05) -> none (scale property) WHY THIS WORKS AS AN ANSWER --------------------------- The tool is a code rewriter. It handled the mechanical parts (directives, PostCSS plugin, package versions, renamed classes in the right order: shadow->shadow-sm only AFTER shadow-sm->shadow-xs) but said it could not migrate the JavaScript config, so the colours, radius and font stayed in the old file, and it did not touch bg-opacity-50 or add a ring colour. None of these produced an error or warning: the build exited 0. The only way to see them is to compare with a baseline, here by computed styles and 4,279 differing pixels. The panel result is the instructive one: the tool converted theme('colors.surface') to var(--color-surface), but the config is still JavaScript and (as far as I could tell) no such CSS variable exists, so the panel lost its background and radius. I built the baseline carefully first: my first config had a nested theme() call that v3 did not resolve, which I only noticed in the baseline probe output.