PostWind

A lightweight layer on top of Tailwind CSS browser runtime. Adds responsive shorthands, unit suffixes, shortcuts, and scroll-triggered animations.

What is PostWind?

PostWind is a ~3KB JavaScript library that extends Tailwind CSS with convenience features for prototyping and static pages. It runs in the browser alongside @tailwindcss/browser@4.

It does not replace Tailwind. Standard Tailwind classes work as-is. PostWind adds:

  • example Pipe responsivep-4|8 or p-4|8|12 for mobile / tablet / desktop
  • example Colon responsivep-4:8 as alias for p-4|8
  • example Unit suffixesp-10px, mt-2rem, w-50% without bracket syntax
  • example @ notationtext-sm@m instead of m:text-sm (breakpoint after class)
  • example Shortcuts — composable class aliases: btn-primary expands to multiple classes
  • example visible: prefix — IntersectionObserver scroll animations
  • example onload: prefix — entrance animations, adds class 100ms after page load
  • example dark: prefix — dark mode via body.dark class
  • example dark-auto — auto-detect OS dark mode preference on body.dark-auto
  • example Container queriesmin-480:flex / max-320:hidden based on element width
  • JS API Body classinit({ body: true }) adds mobile/tablet/desktop to <body>
  • example m: t: d: prefixes — explicit mobile / tablet / desktop breakpoints

How it Works Under the Hood

1. Tailwind generates CSS. PostWind uses Tailwind's browser runtime as its CSS engine. When PostWind needs to know what p-4 produces, it creates a temporary DOM element with that class, waits for Tailwind to process it, then extracts the CSS rules from the generated stylesheet.

2. PostWind re-wraps the CSS. It takes the extracted CSS properties and wraps them in new selectors and media queries. For example, p-4|8 extracts both p-4 and p-8 CSS, then generates:

.p-4\|8 { padding: 1rem; }
@media (min-width: 768px) { .p-4\|8 { padding: 2rem; } }

3. Injected into the page. Generated rules go into <style id="postwind-main"> and <style id="postwind-shortcuts"> elements. A cache prevents duplicate injection.

4. Auto-scan. On DOMContentLoaded, PostWind scans all elements for PostWind-specific classes (pipes, unit suffixes, colon responsive, visible:) and injects their CSS automatically. A MutationObserver watches for dynamically added elements.

Quick Start

<!-- 1. Load PostWind (includes Tailwind) -->
<script src="postwind.js"></script>
<script>
  PostWind.init({
    tailwind: true, // load Tailwind CSS from CDN (default: false)
    shortcuts: {
      'btn': 'px-4 py-2 rounded-lg font-medium',
      'btn-primary': 'btn bg-blue-600 text-white',
    }
  })
</script>
<!-- 2. Use classes directly in HTML -->
<div class="p-4|8 text-lg|2xl">Responsive!</div>
<div class="p-10px w-200px">Pixel values!</div>
<button class="btn-primary">Click me</button>

PostWind.init({ tailwind: true }) loads Tailwind from CDN. Without tailwind: true, PostWind works as a class processor only (Tailwind must be loaded separately). Returns a Promise.

API Reference

Full JavaScript API for PostWind.

PostWind.init(options?) returns Promise

Initializes PostWind. Pass tailwind: true to load Tailwind CSS browser runtime from CDN (default: false). Returns a Promise that resolves when ready.

PostWind.init({
  tailwind: true, // load Tailwind from CDN (default: false)
  breakpoints: {
    m: '@media (max-width: 767px)',   // default
    t: '@media (min-width: 768px)',   // default
    d: '@media (min-width: 1024px)',  // default
    xl: '@media (min-width: 1280px)', // custom
  },
  shortcuts: {
    'btn': 'px-4 py-2 rounded-lg font-medium',
    'btn-primary': 'btn bg-blue-600 text-white',
  }
})
PostWind(className) returns Promise<string|null>

Resolves the CSS for a PostWind class and injects it into the page. Returns the generated CSS string or null. Cached — calling twice with the same class only injects once.

await PostWind('p-4|8')        // pipe responsive
await PostWind('p-10px')       // unit suffix
await PostWind('m:flex')       // breakpoint prefix
await PostWind('btn-primary') // shortcut
PostWind.ready() returns Promise

Returns a Promise that resolves when Tailwind is loaded and ready. Same promise as init() returns.

PostWind.resolve(className) returns Promise<string|null>

Like PostWind() but returns the CSS string without injecting it. Useful for debugging or building custom tooling.

PostWind.twCSS(className) returns Promise<string|null>

Extracts raw CSS properties from Tailwind for a single class. Creates a temp element, waits for Tailwind to process, returns the CSS text (e.g. padding: 1rem;).

PostWind.shortcut(name, classes)

Register a shortcut: a single class name that expands to multiple Tailwind classes. Supports nesting — reference other shortcuts by name. Can be called anytime (in init or after).

PostWind.shortcut('btn', 'px-4 py-2 rounded-lg font-medium')
PostWind.shortcut('btn-primary', 'btn bg-blue-600 text-white')
// then use: <button class="btn-primary">Click</button>
// must call PostWind('btn-primary') to inject CSS
PostWind.breakpoint(name, mediaQuery)

Register or override a named breakpoint. Used by prefix notation (m:, t:, d:) and pipe notation. Can be called anytime.

PostWind.breakpoint('xl', '@media (min-width: 1280px)')
PostWind.breakpoint('print', '@media print')
// then use: await PostWind('xl:text-2xl')

Defaults: m = max-width:767px, t = min-width:768px, d = min-width:1024px

PostWind.observeVisible(element)

Manually observe an element with the IntersectionObserver. Adds pw-visible class when 50% visible. Usually not needed — auto-scan handles elements with visible: classes.

Class Syntax Reference
Syntax Example Equivalent Tailwind
Pipe (2-seg)p-4|8p-4 md:p-8
Pipe (3-seg)p-4|8|12p-4 md:p-8 lg:p-12
Colon (2-seg)p-4:8p-4 md:p-8
Colon (3-seg)p-4:8:12p-4 md:p-8 lg:p-12
Unit suffixp-10pxp-[10px]
Mobile prefixm:hiddenmax-width:767px
Tablet prefixt:flexmd:flex
Desktop prefixd:flexlg:flex
Visiblevisible:opacity-100IntersectionObserver
Dark modedark:bg-gray-900body.dark scoped
@ notationtext-sm@mm:text-sm
Onloadonload:opacity-100class added after 100ms
Container minmin-480:flexResizeObserver
Container maxmax-320:hiddenResizeObserver
Shortcutbtn-primaryuser-defined

Tailwind CSS Examples

Standard Tailwind classes work as-is. PostWind doesn't change anything — these are just Tailwind.

Flexbox

flex gap-4 items-center

Item 1
Taller
Item 3

flex justify-between items-center

Left
Center
Right

flex flex-col md:flex-row gap-4

Column on mobile
Row on tablet+
Resize to test

flex flex-wrap gap-2

Tag 1
Tag 2
Another tag
Tag 4
Longer tag name
Tag 6
Short
Tag 8

flex items-stretch gap-4 (equal height)

Short
This one has more content to make it taller than the others
Medium text here

flex items-center justify-center h-32 (centering)

Perfectly centered

Grid

grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4

1
2
3
4
5
6
7
8

grid grid-cols-3 gap-4 (auto-fit with span)

col-span-2
1 col
1 col
col-span-2
col-span-3 (full width)

Typography

text-5xl font-bold tracking-tight

Heading 5XL

text-3xl font-semibold

Heading 3XL

text-xl font-medium text-gray-700

Subheading XL

text-base leading-relaxed text-gray-600

Body text with relaxed leading. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

text-sm text-gray-500 italic

Small italic caption text

text-xs uppercase tracking-widest font-semibold text-gray-400

Overline label

line-clamp-2 (truncate to 2 lines)

This paragraph is truncated to exactly two lines using line-clamp-2. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.

Colors & Backgrounds

bg-{color}-{shade} — Full Tailwind color palette

50
100
200
300
400
500
600
700
800
900
950
slate
gray
zinc
neutral
stone
red
orange
amber
yellow
lime
green
emerald
teal
cyan
sky
blue
indigo
violet
purple
fuchsia
pink
rose

bg-gradient-to-r from-{color} to-{color}

from-blue-500 to-purple-500
from-pink-500 to-orange-500
from-green-400 via-cyan-500 to-blue-500

Spacing (padding, margin, gap)

p-{n} m-{n} gap-{n} space-y-{n}

p-1
p-2
p-3
p-4
p-6
p-8

p-4 sm:p-6 md:p-8 lg:p-12 xl:p-16

Borders, Shadows & Rounded

rounded-{size}

none
sm
md
lg
xl
2xl
full

shadow-{size}

shadow-sm
shadow
shadow-md
shadow-lg
shadow-xl

border border-{color} ring-{n}

border
border-2 blue
dashed
ring-2 purple
ring + offset

Width & Height

w-full
w-3/4
w-1/2
w-1/3
w-1/4
max-w-xs

w-full sm:w-3/4 md:w-1/2 lg:w-1/3 (responsive)

responsive width

Positioning

relative + absolute positioning

top-2 left-2
top-2 right-2
bottom-2 left-2
bottom-2 right-2
inset-0 centered

Hover, Focus & Transitions

hover:bg-blue-500
hover:shadow-lg hover:-translate-y-1
hover:scale-105
hover:opacity-100
hover:rotate-3

Responsive Show/Hide

block sm:hidden (mobile only)

Overflow & Truncation

truncate

This is a very long text that will be truncated with an ellipsis because it overflows its container width

overflow-auto max-h-24

Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8

Opacity & Aspect Ratio

opacity-{n}

100
75
50
25
10

aspect-{ratio}

square
video

PostWind Responsive

Mobile-first like Tailwind. Pipe is mobile|tablet or mobile|tablet|desktop. Left is base, rest override at breakpoints.

Pipe Notation: Padding

Mobile padding vs tablet+ padding

p-4|12 = p-4 (base) + t:p-12 (768px+)

Pipe Notation: Text Size

Resize window to see text change

text-xl|4xl = text-xl (base) + t:text-4xl (768px+)

Pipe Notation: Grid Columns

A
B
C
D
E
F

grid-cols-1|3 = 1 col (base) + t:3 cols (768px+)

m:, t:, d: Prefixes

m:bg-rose-100 t:bg-amber-100 d:bg-violet-100

m: = max-width:767px | t: = min-width:768px | d: = min-width:1024px

Pipe Notation: Gap & Margin

Box 1
Box 2
Box 3

gap-2|8 = gap-2 (base) + t:gap-8 (768px+)

Multiple Pipes Combined

Multiple pipe classes on one element

p-4|10 text-lg|2xl rounded-xl|3xl

Three-Segment Pipe: mobile | tablet | desktop

Three breakpoints in one class

p-3|6|12 text-base|xl|4xl grid-cols-1|2|4

Three-Segment Grid

A
B
C
D

grid-cols-1|2|4 = 1 col (mobile) + 2 cols (tablet 768px+) + 4 cols (desktop 1024px+)

Unit Suffix Shorthand

Write p-10px instead of p-[10px]. Auto-detected and injected.

Padding with px

p-10px (= p-[10px])
p-20px (= p-[20px])
p-40px (= p-[40px])

p-10px p-20px p-40px

Margin with rem

mt-1rem (= mt-[1rem])
mt-2rem (= mt-[2rem])

mt-1rem mt-2rem

Width with Various Units

w-200px (= w-[200px])
w-50% (= w-[50%])
w-30vw (= w-[30vw])
max-w-500px (= max-w-[500px])

w-200px w-50% w-30vw max-w-500px

Font Size & Line Height

text-14px (= text-[14px])

text-18px (= text-[18px])

text-24px (= text-[24px])

text-1.5rem (= text-[1.5rem])

text-14px text-18px text-24px text-1.5rem

Gap & Border Radius

rounded-8px gap-15px
item 2
item 3

gap-15px rounded-8px

Colon Responsive Shorthand

Use p-4:8 as an alias for p-4|8. Same mobile-first logic.

Two Segments: mobile:tablet

Padding and text change at tablet

p-4:10 text-lg:2xl = p-4|10 text-lg|2xl

Three Segments: mobile:tablet:desktop

Three breakpoints via colons

p-3:6:12 text-base:xl:3xl = p-3|6|12 text-base|xl|3xl

Colon Grid

A
B
C
D

grid-cols-1:2:4 = 1 col (mobile) + 2 cols (768px+) + 4 cols (1024px+)

Colon + Unit Suffix Combined

Unit suffixes work inside pipe/colon notation too

p-10px:20px:40px text-14px:18px:24px

Shortcuts

Compose multiple Tailwind classes into a single reusable class name.

Base & Sizes

btn-xs btn px-2 py-1 text-xs rounded
btn-sm btn px-3 py-1.5 text-xs rounded-md
btn inline-flex items-center justify-center px-4 py-2 rounded-lg font-medium transition-all duration-200 cursor-pointer text-sm
btn-lg btn px-6 py-3 text-base rounded-xl

Solid Buttons

btn-primary btn bg-blue-600 text-white hover:bg-blue-500 active:bg-blue-700
btn-secondary btn bg-gray-200 text-gray-800 hover:bg-gray-300 active:bg-gray-400
btn-danger btn bg-red-600 text-white hover:bg-red-500 active:bg-red-700
btn-success btn bg-emerald-600 text-white hover:bg-emerald-500 active:bg-emerald-700
btn-warning btn bg-amber-500 text-white hover:bg-amber-400 active:bg-amber-600
btn-info btn bg-sky-500 text-white hover:bg-sky-400 active:bg-sky-600
btn-dark btn bg-gray-900 text-white hover:bg-gray-800 active:bg-gray-950
btn-light btn bg-white text-gray-800 hover:bg-gray-50 active:bg-gray-100 border border-gray-200

Outline Buttons

btn-outline btn bg-transparent border-2 border-gray-300 text-gray-700 hover:bg-gray-100
btn-outline-primary btn bg-transparent border-2 border-blue-600 text-blue-600 hover:bg-blue-600 hover:text-white
btn-outline-danger btn bg-transparent border-2 border-red-600 text-red-600 hover:bg-red-600 hover:text-white
btn-outline-success btn bg-transparent border-2 border-emerald-600 text-emerald-600 hover:bg-emerald-600 hover:text-white

Ghost & Link Buttons

btn-ghost btn bg-transparent text-gray-600 hover:bg-gray-100
btn-link btn bg-transparent text-blue-600 hover:underline

Pill Buttons

btn-pill btn rounded-full
btn-pill-primary btn rounded-full bg-blue-600 text-white hover:bg-blue-500
btn-pill-danger btn rounded-full bg-red-600 text-white hover:bg-red-500

Gradient Buttons

btn-gradient btn bg-gradient-to-r from-blue-600 to-purple-600 text-white hover:from-blue-500 hover:to-purple-500
btn-gradient-warm btn bg-gradient-to-r from-orange-500 to-pink-500 text-white hover:from-orange-400 hover:to-pink-400
btn-gradient-cool btn bg-gradient-to-r from-cyan-500 to-blue-500 text-white hover:from-cyan-400 hover:to-blue-400

Shadow Buttons (hover:shadow)

btn-shadow btn bg-white text-gray-800 border border-gray-200 shadow-sm hover:shadow-lg hover:-translate-y-0.5
btn-shadow-primary btn bg-blue-600 text-white shadow-md shadow-blue-500/30 hover:shadow-xl hover:shadow-blue-500/40 hover:-translate-y-0.5
btn-shadow-danger btn bg-red-600 text-white shadow-md shadow-red-500/30 hover:shadow-xl hover:shadow-red-500/40 hover:-translate-y-0.5
btn-shadow-success btn bg-emerald-600 text-white shadow-md shadow-emerald-500/30 hover:shadow-xl hover:shadow-emerald-500/40 hover:-translate-y-0.5

Icon Buttons

btn-icon btn p-2 rounded-lg
btn-icon-round btn p-2 rounded-full

Button Groups (Tailwind)

Badge Shortcuts

Primary Success Danger Warning

badge-primary badge-success badge-danger badge-warning

Card Shortcut

This is a card

Using the card shortcut class.

card = bg-white rounded-xl border border-gray-200 p-6

Visible: Prefix (IntersectionObserver)

Elements animate when they scroll into view (50% threshold). Scroll down to see them appear.

Fade In on Scroll

I fade in when visible (visible:opacity-100)
I fade in slower (duration-1000)
I fade and slide up (visible:opacity-100 visible:translate-y-0)
Scroll down for more visible: examples...

Scale on Scroll

1
Scale + fade
2
With delay
3
More delay
Keep scrolling...

Slide from Sides

Slide from left (visible:translate-x-0)
Slide from right (translate-x-8 visible:translate-x-0)
Slide from below (translate-y-8 visible:translate-y-0)
Almost there...

Background Color on Visible

Red
Green
Blue
Purple
Last one...

Animated Progress Bars

PostWind90%
Tailwind100%
Vanilla CSS60%

@ Notation

Property-first alternative to breakpoint prefixes. Put the breakpoint at the end with @.

Examples

text-sm@m text-2xl@d — small on mobile, large on desktop
flex-col@m flex-row@d
<div class="text-sm@m text-2xl@d">Same as m:text-sm d:text-2xl</div>

onload: Prefix

Adds a class 100ms after page load. Perfect for entrance animations.

Entrance Animations

I fade in after page load (onload:opacity-100)
I scale up and fade in (onload:scale-100 onload:opacity-100)
<div class="opacity-0 transition duration-700 onload:opacity-100">

Container Queries

Per-element responsive rules using ResizeObserver. Based on the element's own width, not the viewport.

Element Width Responsive

Column A
Column B
Column C
(drag to resize — switches to row at 480px wide)
<div class="flex flex-col min-480:flex-row">
<!-- switches to row when this element is >= 480px wide -->

Dark Mode

Toggle .dark on <body> to activate dark: prefixed classes.

Click to add/remove .dark from body

Dark Mode Card

This card adapts its background, text, and border colors when dark mode is active.

Tag 1 Tag 2 Tag 3

Usage

<!-- Add .dark to body -->
<body class="dark">
<div class="bg-white dark:bg-gray-900 text-black dark:text-white">
adapts to dark mode
</div>
</body>
<!-- Toggle via JS -->
document.body.classList.toggle('dark')

Tailwind vs PostWind: Side by Side

Same result, different syntax.

Responsive Padding (2 breakpoints)
Tailwind
px-4 md:px-8
PostWind
px-4|8
Responsive Padding (3 breakpoints)
Tailwind
px-4 md:px-6 lg:px-8
PostWind
px-4|6|8
Responsive Grid
Tailwind
grid-cols-1 md:grid-cols-2 lg:grid-cols-4
PostWind
grid-cols-1|2|4
Responsive Text
Tailwind
text-xl md:text-3xl lg:text-5xl
PostWind
text-xl|3xl|5xl
Show/Hide
Tailwind
hidden md:block
PostWind
m:hidden t:block
Flex Direction
Tailwind
flex flex-col md:flex-row
PostWind
flex m:flex-col t:flex-row

Automated Tests

Testing PostWind breakpoints, pipes, shortcuts, visible: prefix, and dark: mode.

Test Results Running...