Documentation

Tailwind Classes Reference

Complete reference for all Tailwind-style utility classes in Whitehall, with equivalent props and generated Kotlin/Compose output.

Prefer classes over props. Classes are faster to write, more scannable, and consistent with web conventions. Use props only for dynamic/computed values.
// Preferred: class syntax
<Column class="p-16 gap-8 items-center bg-primary rounded-lg">
  <Text class="text-lg font-bold">Title</Text>
</Column>

// Also works: prop syntax (use for dynamic values)
<Column p={dynamicPadding} gap={8}>
  <Text>Title</Text>
</Column>

Scale: Classes and props use a 1:1 scale. The number IS the dp value: p-16 = p={16} = 16dp

Override: Props override class values: class="p-4" p={8} results in 8dp padding

Color classes read the theme. With a theme preset, bg-*, text-*, border-* and the rounded-* scale resolve against the project's tokens at runtime — so bg-card works, and theme="auto" repaints the app without a rebuild. Without one they fall through to Material roles and the Tailwind palette, exactly as documented below.

See it happen

Watch the chain change as classes are added.

Card.wh
<Box class="bg-primary">
  <Text>Card</Text>
</Box>
Kotlin waiting
Scroll into view to compile.

One class, one call: bg-primary becomes a single .background(...) on the Modifier chain.


Spacing

Padding

ClassPropCompose Output
p-{n}p={n}.padding(n.dp)
px-{n}px={n}.padding(horizontal = n.dp)
py-{n}py={n}.padding(vertical = n.dp)
pt-{n}pt={n}.padding(top = n.dp)
pb-{n}pb={n}.padding(bottom = n.dp)
ps-{n}ps={n}.padding(start = n.dp)
pe-{n}pe={n}.padding(end = n.dp)
<Column class="p-16">          // 16dp all sides
<Column class="px-16 py-8">    // 16dp horizontal, 8dp vertical
<Column class="pt-32 pb-16">   // 32dp top, 16dp bottom

Margin

Same pattern as padding (Compose uses outer padding for margin):

ClassPropCompose Output
m-{n}m={n}.padding(n.dp)
mx-{n}, my-{n}, etc.mx={n}, etc.Same pattern as padding

Gap

ClassPropCompose Output
gap-{n}gap={n}Arrangement.spacedBy(n.dp)
<Column class="gap-16">  // 16dp between children
<Row class="gap-8">      // 8dp between children

Sizing

Width

ClassPropCompose Output
w-{n}w={n}.width(n.dp)
w-fullwidth="100%".fillMaxWidth()
w-1/2width="50%".fillMaxWidth(0.5f)
w-1/3width="33%".fillMaxWidth(0.333f)
w-2/3width="67%".fillMaxWidth(0.667f)

Height

ClassPropCompose Output
h-{n}h={n}.height(n.dp)
h-fullheight="100%".fillMaxHeight()
h-1/2height="50%".fillMaxHeight(0.5f)

Size (Both)

ClassPropCompose Output
size-{n}size={n}.size(n.dp)
<Box class="w-full h-64">     // Full width, 64dp height
<Box class="w-1/2">           // 50% width
<Box class="size-48">         // 48dp x 48dp

Layout (Flex)

Cross-axis Alignment (items)

ClassColumnRow
items-startAlignment.StartAlignment.Top
items-centerAlignment.CenterHorizontallyAlignment.CenterVertically
items-endAlignment.EndAlignment.Bottom

Main-axis Arrangement (justify)

ClassCompose Output
justify-startArrangement.Start
justify-centerArrangement.Center
justify-endArrangement.End
justify-betweenArrangement.SpaceBetween
justify-aroundArrangement.SpaceAround
justify-evenlyArrangement.SpaceEvenly

Weight

ClassCompose Output
flex-1.weight(1f)
flex-grow.weight(1f)
<Column class="items-center justify-between">
<Row class="items-center gap-8">
<Box class="flex-1">

Scrollable

ClassPropCompose Output
overflow-scrollscrollable.verticalScroll(rememberScrollState()) (Column)
overflow-y-scrollscrollable="vertical".verticalScroll(rememberScrollState())
overflow-x-scrollscrollable="horizontal".horizontalScroll(rememberScrollState())
<Column class="overflow-scroll p-16">
  <Text>Scrollable content...</Text>
</Column>

<Row scrollable>
  <Image src="1.jpg" />
  <Image src="2.jpg" />
</Row>

Background

Semantic colors

Colors named for the job they do rather than the hue they are. These work under every preset, and they are what to reach for when a color should follow the app's theme instead of being pinned to a value.

NameWhat it is
background / foregroundThe page, and text on it
card / card-foregroundA raised surface, and text on it
popover / popover-foregroundA floating surface, and text on it
primary / primary-foregroundThe main action, and text on it
secondary / secondary-foregroundA quieter action, and text on it
muted / muted-foregroundA recessed fill, and secondary text
accent / accent-foregroundA hover or active fill, and text on it
destructive / destructive-foregroundDanger, and text on it
borderA divider or an outline
inputA field's edge, a step stronger than a border
ringA focus ring

Each takes the usual prefixes, and the usual opacity suffix:

<Column class="bg-background">
  <Card class="bg-card border border-border">
    <Text class="text-card-foreground">Title</Text>
    <Text class="text-muted-foreground">Subtitle</Text>
    <Button class="bg-destructive text-destructive-foreground" text="Delete" />
  </Card>
</Column>

Theme Colors

ClassCompose Output
bg-primary.background(MaterialTheme.colorScheme.primary)
bg-secondary.background(MaterialTheme.colorScheme.secondary)
bg-surface.background(MaterialTheme.colorScheme.surface)
bg-error.background(MaterialTheme.colorScheme.error)
bg-transparent.background(Color.Transparent)
bg-white.background(Color.White)
bg-black.background(Color.Black)

Hex Colors

ClassCompose Output
bg-[#RRGGBB].background(Color(0xFFRRGGBB))

Tailwind Palette Colors

Full Tailwind color palette with shades 50-950:

ClassHex ValueCompose Output
bg-yellow#EAB308 (yellow-500).background(Color(0xFFEAB308))
bg-yellow-100#FEF9C3.background(Color(0xFFFEF9C3))
bg-yellow-500#EAB308.background(Color(0xFFEAB308))
bg-yellow-900#713F12.background(Color(0xFF713F12))
bg-blue-500#3B82F6.background(Color(0xFF3B82F6))
bg-red-500#EF4444.background(Color(0xFFEF4444))
bg-green-500#22C55E.background(Color(0xFF22C55E))

Available colors: slate, gray, zinc, neutral, stone, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose

Available shades: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950

Opacity Modifier

Use /N suffix (0-100) to apply alpha transparency:

ClassCompose Output
bg-red-500/20.background(Color(0x33EF4444))
bg-blue/50.background(Color(0x803B82F6))
bg-white/80.background(Color(0xCCFFFFFF))
bg-black/10.background(Color(0x1A000000))
bg-primary/50.background(MaterialTheme.colorScheme.primary.copy(alpha = 0.5f))
<Box class="bg-primary">
<Box class="bg-[#FF5722]">
<Box class="bg-yellow">           // yellow-500
<Box class="bg-yellow-100">       // light yellow
<Box class="bg-red-500/20">       // red at 20% opacity

Gradients

ClassPurpose
bg-gradient-to-r, -l, -t, -bLinear gradient along an axis
bg-gradient-to-tr, -tl, -br, -blLinear gradient along a diagonal
from-{color}First stop
via-{color}Optional middle stop
to-{color}Last stop

Compiles to Brush.linearGradient(...), with the direction suffix mapped to Offsets. Stop colors use the same grammar as bg-* — theme tokens, Material roles, the Tailwind palette, arbitrary hex — each with the same /opacity suffix. A missing from-*/to-* stop defaults to transparent, matching Tailwind. bg-gradient-to-* wins over a plain bg-* on the same element, the way a CSS background-image layers over a background-color.

<Box class="bg-gradient-to-r from-blue-500 to-purple-500">
<Box class="bg-gradient-to-br from-yellow-400 via-orange-500 to-red-500">
<Card class="bg-gradient-to-b from-primary to-secondary">

Background images

A background can be an image as well as a colour or a gradient. The image itself is one value; everything about how it is drawn is its own class, the way Tailwind splits CSS's shorthand.

<Box background="url(/backgrounds/city.jpg)" class="bg-cover bg-center" />
ClassControls
bg-cover bg-contain bg-auto bg-[85%]How much of the source is sampled
bg-center bg-top bg-bottom-right bg-[30%_40%]Where the sampled rect sits
bg-repeat bg-no-repeat bg-repeat-x bg-repeat-yTiling
bg-fixed bg-localPositioned against the window, or against the element

Paths resolve against static/ at build time. backgroundPositionX and backgroundPositionY are morphable, which is how a background parallaxes against a scroll.

Put the image on the prop, not in a class. Tailwind's arbitrary-value form bg-[url(/photo.jpg)] is documented but does not currently compile: the class is split on / to find an opacity — Tailwind's bg-red-500/20 spelling — before the brackets are considered, so the path is read as an alpha value and rejected. Since a static/ path starts with /, that is most of them.

Blur, Blend and Clipping

blur-*

Blurs the element's own content — its fill, its border, anything inside it. Tailwind's names and Tailwind's values.

ClassRadius
blur-xs blur-sm blur-md blur-lg4 / 8 / 12 / 16 dp
blur-xl blur-2xl blur-3xl24 / 40 / 64 dp
blur8 dp — Tailwind v3's default, kept as an alias
blur-noneEmits nothing
blur-[17] blur-[17px]17 dp

Like CSS's filter: blur(), it spreads outside the element's box. A rounded-* or overflow-hidden on the same element clips it back, exactly as overflow: hidden does on the web.

backdrop-blur-*

Blurs what is painted behind the element — a frosted panel over a photo or a scrolling list. Same scale, same names.

<Box class="w-full h-full">
  <Image src="/backgrounds/city.jpg" fit="cover" class="w-full h-full" />
  <Box class="backdrop-blur-xl bg-white/20 rounded-3xl p-32">
    <Text class="text-5xl">Blur!</Text>
  </Box>
</Box>

Nothing marks the content being sampled. The compiler works out what to blur: everything painted below the element in the same markup tree — preceding siblings at every level up the chain, which is CSS's own definition of a backdrop.

  • Put the class where the backdrop is visible. A component whose own root says backdrop-blur-xl with nothing behind it in its own file is a build error. Pass the class at the use site instead — <FrostedCard class="backdrop-blur-xl" />.
  • Name a pair when a screen has more than one, or to bound what gets recorded: backdrop/photo on the source, backdrop-blur-xl/photo on the panel. Tailwind's group/edit spelling.
  • A child does not sample its own parent — recording an ancestor would record the consumer inside it. Put a background meant to be blurred beside the panel, not around it.

mix-blend-*

Blends the element with what is beneath it. Seventeen of Tailwind's eighteen names, mapped to Compose's BlendMode.

<Box class="mix-blend-plus-lighter" background="color(srgb-extended 2 2 2)" />
<Box class="mix-blend-soft-light bg-black/12" />

The lowering is picked per element: a fill with no children blends straight into the parent's canvas — one draw, no buffer — and anything else gets an offscreen layer, which is unavoidable, since you cannot blend a composite that does not exist yet.

mix-blend-plus-darker has no Compose equivalent and is a build error rather than a silent no-op. And mix-blend-* on an element that something below it blurs is refused: being recorded gives it a canvas of its own, so the blend would quietly do nothing.

overflow-hidden

Clips children to the element's bounds, including a blur-* that would otherwise spread past them. Pairs with rounded-* to clip to the corner radius.

<Box class="overflow-hidden rounded-2xl">
  <Image src={url} fit="cover" />
</Box>

Border Radius

Classdp ValueCompose Output
rounded-none0dpRoundedCornerShape(0.dp)
rounded-sm2dpRoundedCornerShape(2.dp)
rounded4dpRoundedCornerShape(4.dp)
rounded-md6dpRoundedCornerShape(6.dp)
rounded-lg8dpRoundedCornerShape(8.dp)
rounded-xl12dpRoundedCornerShape(12.dp)
rounded-2xl16dpRoundedCornerShape(16.dp)
rounded-3xl24dpRoundedCornerShape(24.dp)
rounded-fullCircleCircleShape

Side-specific

ClassDescription
rounded-t-{size}Top corners only
rounded-b-{size}Bottom corners only
rounded-s-{size}Start corners only
rounded-e-{size}End corners only
<Card class="rounded-lg">
<Box class="rounded-full">
<Card class="rounded-t-xl">

Border

Border Width

ClassCompose Output
border.border((1f / density).dp, ...)
border-0No border
border-2.border(2.dp, ...)
border-4.border(4.dp, ...)

A bare border is Tailwind's own default weight, 1 px — one physical pixel, not a flat 1.dp. At a fractional density (2.625× on the reference device) a flat 1.dp line rounds up to 3 physical pixels, nearly three times the hairline the browser renders. (1f / density).dp converts back to exactly one physical pixel whatever the density is. Every other width is a literal dp value; the overshoot is only dramatic at 1px.

Directional Borders

ClassEffect
border-tTop edge only, one physical pixel
border-bBottom edge only, one physical pixel
border-lLeft edge only, one physical pixel
border-rRight edge only, one physical pixel
border-xLeft and right edges
border-yTop and bottom edges
border-t-2, border-b-4, …Any edge with an explicit dp width

There is no per-edge Modifier.border in Compose, so a directional border compiles to a drawBehind that strokes only the requested edges. The border reserves its own space, the way a border-box edge adds to a content-sized element's height on the web: each active edge also gets a .padding() (for t/b) or .absolutePadding() (for l/r) — absolute, not start/end, because Tailwind's l/r are the physical sides, not the logical ones ps-/pe- already claim.

<Box class="border-b p-16">                    // hairline bottom rule
<Row class="border-t-2 border-primary">        // 2dp top rule, theme color
<Column class="border-x-4">                    // 4dp on both left and right

Border Colors

ClassCompose Output
border-primary.border(..., MaterialTheme.colorScheme.primary)
border-outline.border(..., MaterialTheme.colorScheme.outline)
border-white.border(..., Color.White)
border-black.border(..., Color.Black)
border-yellow.border(..., Color(0xFFEAB308))
border-blue-300.border(..., Color(0xFF93C5FD))
border-black/10.border(..., Color(0x1A000000))

Edges share whatever border-{color} sets (or MaterialTheme.colorScheme.outline by default). There is no per-edge color — Tailwind's border-t-red-500 has no equivalent here.

<Box class="border-2 border-primary rounded-lg">
<Box class="border border-yellow-300 rounded">
<Box class="border border-black/10">   // subtle border

Shadow & Opacity

Shadow

With a theme, shadow-* is Tailwind's real box shadow — offset, blur, spread and color, most steps stacking two layers. Without one it stays the Material elevation it always was, so nothing that predates themes changes.

ClassCSS (Tailwind v4)Elevation under preset = "none"
shadow-nonenoneNo shadow
shadow-2xs0 1px rgb(0 0 0 / 0.05)No shadow
shadow-xs0 1px 2px 0 rgb(0 0 0 / 0.05)2dp
shadow-sm0 1px 3px 0 …, 0 1px 2px -1px …2dp
shadowTailwind v3's shadow, which v4 renamed shadow-sm4dp
shadow-md0 4px 6px -1px …, 0 2px 4px -2px …6dp
shadow-lg0 10px 15px -3px …, 0 4px 6px -4px …10dp
shadow-xl0 20px 25px -5px …, 0 8px 10px -6px …16dp
shadow-2xl0 25px 50px -12px rgb(0 0 0 / 0.25)24dp
Elevation and box shadow do not convert. Android's shadow is a height: you give it a number and the platform derives blur and offset from a fixed light position. CSS gives you the shadow directly, and Tailwind's scale uses all four parameters including negative spread. shadow-lg is not "10dp of elevation" — it is two specific shadows. Compose 1.9's Modifier.dropShadow takes exactly CSS's parameters, so the scale is carried across as written rather than approximated.

The bare shadow keeps pointing at the same visual weight it always had, so existing projects do not silently change when v4 shifted the whole scale down a step.

Opacity

ClassAlphaCompose Output
opacity-00%.alpha(0f)
opacity-2525%.alpha(0.25f)
opacity-5050%.alpha(0.5f)
opacity-7575%.alpha(0.75f)
opacity-100100%.alpha(1f)
<Card class="shadow-lg opacity-75">

Safe Area

Handle system UI insets (status bar, navigation bar):

ClassPropCompose Output
safesafeArea.windowInsetsPadding(WindowInsets.systemBars)
safe-topsafeTop.windowInsetsPadding(WindowInsets.statusBars)
safe-bottomsafeBottom.windowInsetsPadding(WindowInsets.navigationBars)
<Column class="safe-top p-16">   // Avoid status bar overlap
<Column class="safe">            // Avoid both bars
<Column safeTop safeBottom>      // Prop syntax

Typography

Font Size

Classsp ValueCompose Output
text-xs12spfontSize = 12.sp
text-sm14spfontSize = 14.sp
text-base16spfontSize = 16.sp
text-lg18spfontSize = 18.sp
text-xl20spfontSize = 20.sp
text-2xl24spfontSize = 24.sp
text-3xl30spfontSize = 30.sp
text-4xl36spfontSize = 36.sp

Font Weight

ClassCompose Output
font-thinFontWeight.Thin
font-lightFontWeight.Light
font-normalFontWeight.Normal
font-mediumFontWeight.Medium
font-semiboldFontWeight.SemiBold
font-boldFontWeight.Bold
font-extraboldFontWeight.ExtraBold
font-blackFontWeight.Black

All nine weights are declared against the theme font's wght axis, so font-bold gets the typeface's real bold rather than a synthesized smear of the Regular master.

Font Family

ClassCompose Output
font-monofontFamily = FontFamily.Monospace

FontFamily.Monospace is a Compose constant, not a project setting — the font key in Theming configures the body typeface, which is a different axis, so font-mono does not go through the theme layer. font-sans stays unmapped: in Tailwind it undoes a browser default Compose never had.

Letter Spacing

ClassValue
tracking-tighter-0.05em
tracking-tight-0.025em
tracking-normal0em
tracking-wide0.025em
tracking-wider0.05em
tracking-widest0.1em

Tailwind's scale is in em, which maps directly onto Compose's TextStyle.letterSpacing. tracking-normal is an explicit 0em, not "unset" — Material's bodyLarge otherwise leaks letterSpacing = 0.5sp into every <Text>.

<Text class="text-xs tracking-wide font-medium">Section Label</Text>

Font Style & Decoration

ClassCompose Output
italicFontStyle.Italic
not-italicFontStyle.Normal
underlineTextDecoration.Underline
line-throughTextDecoration.LineThrough
no-underlineTextDecoration.None

Line Height (Leading)

ClassLine HeightDescription
leading-none1emNo extra spacing
leading-tight1.25emTight spacing
leading-snug1.375emSnug spacing
leading-normal1.5emNormal spacing (default)
leading-relaxed1.625emRelaxed spacing
leading-loose2emDouble spacing
leading-{n}n.spFixed sp value

Text Alignment

ClassCompose Output
text-leftTextAlign.Start
text-centerTextAlign.Center
text-rightTextAlign.End
text-justifyTextAlign.Justify

Text Color

ClassCompose Output
text-primaryMaterialTheme.colorScheme.primary
text-secondaryMaterialTheme.colorScheme.secondary
text-whiteColor.White
text-blackColor.Black
text-[#RRGGBB]Color(0xFFRRGGBB)
text-yellowColor(0xFFEAB308)
text-blue-900Color(0xFF1E3A8A)
text-white/80Color(0xCCFFFFFF)

Text Overflow

ClassCompose Output
truncateoverflow = TextOverflow.Ellipsis, maxLines = 1
line-clamp-{n}maxLines = n
<Text class="text-lg font-bold text-primary">Title</Text>
<Text class="text-sm italic text-center">Subtitle</Text>
<Text class="truncate">Long text that gets cut off...</Text>
<Text class="line-clamp-2">Text limited to 2 lines...</Text>
<Text class="text-lg leading-relaxed">Comfortable reading</Text>

Complete Example

<Column class="p-16 gap-16 bg-surface">
  <Text class="text-2xl font-bold text-primary">Welcome</Text>

  <Card class="p-16 rounded-lg shadow-md">
    <Column class="gap-8">
      <Text class="text-lg font-medium">Card Title</Text>
      <Text class="text-sm text-secondary line-clamp-2">
        Description text that might be long...
      </Text>
    </Column>
  </Card>

  <Row class="gap-8 items-center justify-between">
    <Button class="flex-1" text="Cancel" />
    <Button class="flex-1 bg-primary" text="Submit" />
  </Row>
</Column>

See Also