Skip to content

Simple Tailwind utilities for complicated CSS.

Text that always hits your contrast target, whatever is behind it. Gradient borders as plain classes. Any color lightened, darkened or hue-shifted in place. Halftone and CRT textures, no image files. One stylesheet, no plugin, no config.

@import 'tw-jib-css';

or only the part you want: tw-jib-css/automatic-contrast

Text that adapts
to any background.

Pick a text color by hand and you own it forever, through every palette tweak and every theme after. Name the level instead, AA, AAA or AA Large, and CSS solves the color from whatever background is actually under it.

text-contrast-aa-lgbold titles and headings

Bold Brand Title
Bold Brand Title
Bold Brand Title
Bold Brand Title
Bold Brand Title
Bold Brand Title

text-contrast-aabody copy, the usual choice

Body copy, readable anywhere
Body copy, readable anywhere
Body copy, readable anywhere
Body copy, readable anywhere
Body copy, readable anywhere
Body copy, readable anywhere

text-contrast-aaasmall text, the strictest level

Small text, strictest level
Small text, strictest level
Small text, strictest level
Small text, strictest level
Small text, strictest level
Small text, strictest level
css
<div class="bg-violet-600 text-contrast-aa"> Auto-contrasted text </div> <div class="bg-amber-400 text-contrast-aaa"> Stricter AAA contrast </div> <div class="bg-emerald-700 text-contrast-aa-lg"> Large-text ratio </div>
ChromiumFirefoxSafariexact on 242 colors, all three levels

The shade is solved, not searched. Invert the ratio formula for a target luminance, build a color that carries it. Plain relative color syntax, so it ships from the main entry.

It lands on the threshold with no padding, so a pixel-sampled reading can sit a few thousandths either side. Want headroom? Raise --jib-contrast-ratio-* and every utility follows.

Accessible Shade docs →
gradient border, gradient body, one element

Gradient borders without the hacks.

Every AI chat input has one. Solved several times over, and every recipe has a catch. border-image cannot round a corner. mask-composite has the thinnest support. This runs the one that works, padding-box over border-box, which by hand is the string on the left. Linear, radial or conic across eight interpolation modes, and the transparent border comes free.

html
<!-- the arbitrary-value version -->
<div class="border-2 border-transparent rounded-xl
  [background:linear-gradient(#fff,#fff)_padding-box,
   linear-gradient(to_right,#3b82f6,#a855f7)_border-box]">

<!-- the same border, this module -->
<div class="border-2 rounded-xl
  border-linear-to-r border-from-blue-500 border-to-purple-500">

Lighten, desaturate, rotate. Or all three.

You want blue-500 a step darker on hover. The usual answer is a new palette entry to name, document and remember. Write bg-darken-15 instead and it derives from the color already on the element. Lighten, darken, saturate, desaturate, hue-shift, on all seven color properties. Every amount is a registered @property, so it transitions.

html
<div class="bg-blue-500 bg-lighten-30">
<div class="bg-blue-500 bg-darken-40">
<div class="bg-blue-500 bg-hue-rotate-90">
<div class="bg-blue-500 -bg-saturation-50">

/* compose them */
<div class="bg-blue-500 bg-lighten-20
  bg-hue-rotate-45 -bg-saturation-20">
/oklch
/hsl
/lab
/lch
/oklab
/hwb
/rgb
/srgb
/srgb-linear
/display-p3
/a98-rgb
/prophoto-rgb
/rec2020
/xyz
/xyz-d50
/xyz-d65

Seventeen color spaces, one modifier.

Lighten a yellow in HSL and it blows out to white. Lighten a blue the same amount and it barely moves. That is HSL, not you. Append /oklch and one number means one visible step on every hue. Append /hsl when the uneven look is the one you want.

html
<div class="bg-red-500 bg-lighten-40/oklch">
<div class="bg-red-500 bg-lighten-40/hsl">
<div class="bg-red-500 bg-lighten-40/lab">
<div class="bg-red-500 bg-lighten-40/display-p3">
the source color, cycling

comic-gap-1 comic-bleed-2

pixel-gap-0 pixel-bloom-2

no modifiers

no modifiers

comic-dot-1 comic-gap-10 comic-bleed-0

pixel-size-6 pixel-bloom-8

CMYK dots and RGB pixels, faithfully reproduced.

Not an overlay. Comic splits your color into CMYK dot grids and multiplies them back, the way a press prints. Pixel splits it into RGB columns and screens them, the way a display emits light. Left alone both read back as your color. Turn them up and the channels come apart. Ink holds its edge, emitters glare, which is why big dots want bleed at zero and big pixels want bloom cranked.

html
<!-- working size: reads back as the color -->
<div class="bg-comic-indigo-600">
<div class="bg-pixel-indigo-600">

<!-- turned up: the channels show -->
<div class="bg-comic-indigo-600 comic-dot-1 comic-gap-10 comic-bleed-0">
<div class="bg-pixel-indigo-600 pixel-size-6 pixel-bloom-8">

Ripples on tap. Zero JavaScript.

Most ripples need JavaScript. A listener, a click coordinate, a node you append and then remember to remove. This is a radial gradient and two @property transitions. Press sets the values instantly, release eases them back, so the ripple finishes even on a click you barely register. Click the demos.

html
<button class="bg-indigo-600 bg-ripple
  ripple-color-white/40
  ripple-position-center
  ripple-duration-60">

ripple-color-rose-500/50
ripple-duration-30
ripple-position-top

Also Included.

Two modules that touch no color, and one package that deliberately isn't in this one.

Border Styles

Tailwind sets border style on all four sides at once. These do one side at a time. A dashed top over a dotted bottom, no custom rule.

html
<div class="border-t-dashed border-b-dotted"> <div class="border-l-double border-r-solid"> <div class="border-x-dashed border-y-dotted">
Border Style docs →

Named Grid Areas

Tailwind has grid-cols and grid-rows but no grid-template-areas. Name the regions in the markup, place children by name, stop counting line numbers.

html
<div class="grid grid-template-areas-['head_head'_'side_main']"> <header class="grid-area-[head]"> <aside class="grid-area-[side]">
Grid docs →

Cutting-Edge CSS

separate package

A second, optional install for the newest things CSS can do. Corner shapes past the circular arc, animation to auto, a styleable select, a live WCAG badge, and the color transforms as callable CSS functions.

Nothing above depends on it. Half of it does replace utilities on this page with @function versions, so that half is its own opt-in import.

css
@import 'tailwindcss'; @import 'tw-jib-css'; @import 'tw-jib-css-experimental';
Experimental docs →