tailwind-runtime
v0.0.1
Published
Tailwind runtime, access values and convert classes to styles
Downloads
1
Readme
Tailwind Runtime
Interact with a tailwindcss 4.x bundle at runtime using the CSS Object Model APIs:
- Convert tailwind class names to style objects and inline css
- Substitutes
var()and simplifies somecalc() - Performs calculations without the DOM, never triggers a reflow
- Supports variants and pseudo classes like
hover:, etc - Tiny, zero dependencies, typescript support
Note: Tailwind runtime is not a JIT compiler, it uses tailwind's output as its source of knowlege, so any classes or values not being generated by your existing tailwind configuration will not be available.
Install from npm:
npm i tailwind-runtimeExamples
Set up, automatically finding tailwind in the document:
import { TailwindRuntime } from 'tailwind-runtime';
const tw = new TailwindRuntime();Set up, providing a stylesheet containing a tailwind build:
import { TailwindRuntime } from 'tailwind-runtime';
const tw = new TailwindRuntime({
tailwind: document.styleSheets[0],
});Convert class names to a style object or inline css, the order of class names doesn't matter and will be sorted:
tw.toObject('px-8 m-4 scale-50');
// -> {margin: '1rem', scale: '50% 50%', paddingInline: '2rem'}
tw.toCSS('px-8 m-4 scale-50');
// -> 'margin: 1rem; scale: 50% 50%; padding-inline: 2rem;'Simulate states to get variant styles:
const names = 'text-black m-auto hover:text-white';
tw.toObject(names, []);
// -> {margin: 'auto', color: '#000'}
tw.toObject(names, ['hover']);
// -> {margin: 'auto', color: '#fff'}Tailwind runtime doesn't care about conditionals like selectors and media queries, all variants are explicit. This is intentional because inline styles can't have variants, and lets you simulate different states for testing. For example here the second rule won't apply unless you tell it to, even though [*] is a variant that should match all elements:
tw.toCSS('p-[1em] [*]:p-[2em]');
// -> 'padding: 1em;'
tw.toCSS('p-[1em] [*]:p-[2em]', ['[*]']);
// -> 'padding: 2em;'The final boolean parameter for toObject and toCSS will only include classes that explicitly match the given state, useful for seperating class names by state:
const names = 'text-left p-4 hover:text-right';
tw.toCSS(names, ['hover'], true);
// -> 'text-align: right;'Find theme values:
tw.getValue('--blur-md');
// -> '12px'
tw.getValue('--spacing');
// -> '0.25rem'Access theme namespaces as objects by creating proxies:
const fonts = tw.themeNamespace('font-weight');
const aspectRatios = tw.themeNamespace('aspect');
Object.keys(fonts);
// -> ['thin', 'extralight', 'light', 'normal', 'medium', 'semibold', 'bold', 'extrabold', 'black']
aspectRatios['video'];
// -> '16 / 9'Breakpoint and colour namespaces are available through shortcuts:
Object.entries(tw.breakpoints);
// -> [['sm', '40rem'], ['md', '48rem'], ['lg', '64rem'], ['xl', '80rem'], ['2xl', '96rem']]
Object.keys(tw.colors).length;
// -> 244Find the subset of known breakpoints that are currently active based on the viewport width:
tw.activeBreakpoints;
// -> ['sm', 'md', 'lg']
const names = 'p-[1em] md:p-[2em] lg:p-[3em] ';
tw.toCSS(names, tw.activeBreakpoints);
// -> 'padding: 3em;'Variables and properties
Tailwind runtime tries to allow weird use cases and has logic for forwarding or removing substituted variable values based on tailwind's custom property definitions. Here are a few edge case examples you shouldn't need to worry about unless you're doing something cursed.
We try to substitute local versions of variables, but keep their definitions in the output unless tailwind defines a property marking them as not inherited:
tw.toCSS('hover:[--color-white:#f00] text-white');
// -> 'color: #fff;'
tw.toCSS('hover:[--color-white:#f00] text-white', ['hover']);
// -> 'color: #f00; --color-white: #f00;'Notice though how even though tailwind's scale utilities use variables internally, their definitions are removed after substitution because they're marked as not inherited by @property rules:
tw.findUtilityRule('scale-90').cssText;
// -> '.scale-90 { --tw-scale-x: 90%; --tw-scale-y: 90%; --tw-scale-z: 90%; scale: var(--tw-scale-x) var(--tw-scale-y); }'
tw.toCSS('scale-90');
// -> 'scale: 90% 90%;'As of 4.0, tailwind doesn't generate a @property rule for the --tw-drop-shadow variable used by filter utilities unless drop-shadow utilities are also explicitly used, so even though many other variables get hidden by tailwind runtime, this one will still be forwarded because it could be set elsewhere:
tw.getValue('--blur-md');
// -> '12px'
tw.findUtilityRule('blur-md').cssText;
// -> '.blur-md { --tw-blur: blur(var(--blur-md)); filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); }'
tw.findUtilityRule('grayscale').cssText;
// -> '.grayscale { --tw-grayscale: grayscale(100%); filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); }'
tw.toCSS('blur-md grayscale');
// -> 'filter: blur(12px) grayscale(100%) var(--tw-drop-shadow,);'Importantly though, all of this information comes from your tailwind build, so any changes to these semantics between versions should be reflected here automatically.
