tailwind2-9 Exercise 3: Logical Properties, RTL and Print
==============================================================
Setup: a folder with npm install tailwindcss @tailwindcss/cli and in.css containing @import "tailwindcss"; Build with
node node_modules/@tailwindcss/cli/dist/index.mjs -i in.css -o out.css
The scripts below use my own small DevTools driver (cdp.mjs: real key presses and clicks, Emulation.setEmulatedMedia, raw send for Accessibility and Page.printToPDF). If you do not have it, the same checks can be made by hand in
Chrome DevTools: Rendering panel (emulate prefers-reduced-motion, prefers-contrast, forced-colors, print), Elements > Accessibility tab (computed name), and File > Print.
--- logical and rtl section of page.html ---
logical
physical
rtl/ltr
start-0 end-0
--- r5.mjs ---
import { withPage } from '../../c4/cdp.mjs';
await withPage('file:///C:/lwdj/tw2/c9/a/page.html', async (p) => {
const box = async (id) => ['margin-left','margin-right','padding-left','padding-right','border-left-width','border-right-width','text-align'].map(async k=> k.replace('-width','').replace('margin-','m-').replace('padding-','p-')+'='+await p.css('#'+id,k));
const show = async (label,id)=> console.log(label.padEnd(26), (await Promise.all(await box(id))).join(' '));
await show('LTR logical (l1)','l1'); await show('LTR physical (l2)','l2');
console.log('LTR rtl/ltr variants (l3) bg', await p.css('#l3','background-color'), '| margin-left', await p.css('#l3','margin-left'));
console.log('LTR start-0 left', await p.ev(`Math.round(document.querySelector('#l4 span:nth-child(1)').getBoundingClientRect().left)`), '| end-0 left', await p.ev(`Math.round(document.querySelector('#l4 span:nth-child(2)').getBoundingClientRect().left)`));
await p.ev(`document.getElementById('logical').dir='rtl'`);
await show('RTL logical (l1)','l1'); await show('RTL physical (l2)','l2');
console.log('RTL rtl/ltr variants (l3) bg', await p.css('#l3','background-color'), '| margin-left', await p.css('#l3','margin-left'), '| margin-right', await p.css('#l3','margin-right'));
console.log('RTL start-0 left', await p.ev(`Math.round(document.querySelector('#l4 span:nth-child(1)').getBoundingClientRect().left)`), '| end-0 left', await p.ev(`Math.round(document.querySelector('#l4 span:nth-child(2)').getBoundingClientRect().left)`));
console.log('PRINT screen: p1 display', await p.css('#p1','display'), '| p2', await p.css('#p2','display'), '| p3 bg', await p.css('#p3','background-color'));
await p.media([], 'print');
console.log('PRINT print : p1 display', await p.css('#p1','display'), '| p2', await p.css('#p2','display'), '| p3 bg', await p.css('#p3','background-color'), '| p3 color', await p.css('#p3','color'));
});
Results
LTR logical (l1) m-left=16px m-right=32px p-left=8px p-right=24px border-left=4px border-right=0px text-align=start
LTR physical (l2) m-left=16px m-right=32px p-left=8px p-right=24px border-left=4px border-right=0px text-align=left
LTR l3 bg green, margin-left 0px ; start-0 span left 16, end-0 span left 826
RTL logical (l1) m-left=32px m-right=16px p-left=24px p-right=8px border-left=0px border-right=4px text-align=start
RTL physical (l2) unchanged from LTR
RTL l3 bg red (rtl:bg-red-500), margin-right 40px (rtl:ms-10) ; start-0 span left 822, end-0 span left 16
PRINT screen: nav display block, "only in print" display none, p3 yellow ; print media: nav none, "only in print" block, p3 rgb(255,255,255) with text rgb(0,0,0)
Logical-axis utilities built (b/p.html, 4.3.3): pbs-4 pbe-1 mbs-3 mbe-2 inline-4 block-8 min-inline-2 max-block-10 border-bs-2 inset-s-1 inset-bs-0 start-0 border-s-2 ps-2 all produce a rule;
pis-2, pie-3, mis-1, mie-4 produce nothing.
--- print pages: a.html (nav, three h-80 sections), b.html (nav has print:hidden), c.html (b + break-before-page on sections two and three) ---
site navigation (no print: classes)
b.html: the nav line becomes
c.html: the 2nd and 3rd sections gain break-before-page
--- pdf.mjs ---
import { withPage } from '../../c4/cdp.mjs';
import fs from 'node:fs';
for (const f of ['a','b','c']) {
await withPage(`file:///C:/lwdj/tw2/c9/pr/${f}.html`, async (p) => {
const r = await p.send('Page.printToPDF', { paperWidth: 8.27, paperHeight: 11.69, printBackground: true, marginTop: 0.4, marginBottom: 0.4, marginLeft: 0.4, marginRight: 0.4 });
const buf = Buffer.from(r.data, 'base64'); fs.writeFileSync(`${f}.pdf`, buf);
const pages = (buf.toString('latin1').match(/\/Type\s*\/Page[^s]/g) || []).length;
console.log(f + '.html (A4, 0.4in margins):', buf.length, 'bytes,', pages, 'PDF page(s)');
});
}
a.html (A4, 0.4in margins): 13332 bytes, 2 PDF page(s)
b.html (A4, 0.4in margins): 10476 bytes, 1 PDF page(s)
c.html (A4, 0.4in margins): 11631 bytes, 3 PDF page(s)
print-color-adjust-exact: no rule generated; the arbitrary property [print-color-adjust:exact] generates print-color-adjust: exact;
WHY THIS WORKS AS AN ANSWER
---------------------------
Under dir="rtl" the logical block mirrored (margins swap, the border moves to the right, start-0 moves to the right edge) while the physical block did not, which is the whole case for ms-/ps-/border-s/start- over ml-/pl-/border-l/left.
rtl: and ltr: handled the rest. The print results show the print: variant in two ways: computed styles under emulated print media, and real page breaks in the PDF: the navigation plus three 320 px sections made two pages; hiding the
navigation with print:hidden made one; break-before-page forced three. My first PDF test used shorter sections and gave one page in every case, which showed nothing: the sections were made taller so the difference could appear.
(Page counts were read by counting /Type /Page objects in the PDF bytes, a rough method that worked here.)