---
title: "Tailwind CSS 4.1: text shadows, masks and safer utilities · vuejs.school"
description: "Tailwind CSS 4.1 adds text shadows, mask utilities, input device variants and fallbacks for older browsers. Here is what shipped and three uses in a Vue component."
canonical_url: "https://vuejs.school/community/blog/tailwind-css-4-1-text-shadows-and-masks"
meta:
  "og:description": "Tailwind CSS 4.1 adds text shadows, mask utilities, input device variants and fallbacks for older browsers. Here is what shipped and three uses in a Vue component."
  "og:image": "https://vuejs.school/_og/d/a_~VGFpbHdpbmQgQ1NTIDQuMTogdGV4dCBzaGFkb3dzLCBtYXNrcyBhbmQgc2FmZXIgdXRpbGl0aWVzIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~VGFpbHdpbmQgQ1NTIDQuMTogdGV4dCBzaGFkb3dzLCBtYXNrcyBhbmQgc2FmZXIgdXRpbGl0aWVz%2Cdescription_~VGFpbHdpbmQgQ1NTIDQuMSBhZGRzIHRleHQgc2hhZG93cywgbWFzayB1dGlsaXRpZXMsIGlucHV0IGRldmljZSB2YXJpYW50cyBhbmQgZmFsbGJhY2tzIGZvciBvbGRlciBicm93c2Vycy4gSGVyZSBpcyB3aGF0IHNoaXBwZWQgYW5kIHRocmVlIHVzZXMgaW4gYSBWdWUgY29tcG9uZW50Lg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_lilac%2Cp_Ii9jb21tdW5pdHkvYmxvZy90YWlsd2luZC1jc3MtNC0xLXRleHQtc2hhZG93cy1hbmQtbWFza3Mi%2Cs_5yWWoTduQve1FFlv.png"
  "og:image:alt": "Tailwind CSS 4.1: text shadows, masks and safer utilities · vuejs.school"
  "og:image:height": "600"
  "og:image:type": image/png
  "og:image:width": "1200"
  "og:title": "Tailwind CSS 4.1: text shadows, masks and safer utilities · vuejs.school"
  "twitter:image": "https://vuejs.school/_og/d/a_~VGFpbHdpbmQgQ1NTIDQuMTogdGV4dCBzaGFkb3dzLCBtYXNrcyBhbmQgc2FmZXIgdXRpbGl0aWVzIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~VGFpbHdpbmQgQ1NTIDQuMTogdGV4dCBzaGFkb3dzLCBtYXNrcyBhbmQgc2FmZXIgdXRpbGl0aWVz%2Cdescription_~VGFpbHdpbmQgQ1NTIDQuMSBhZGRzIHRleHQgc2hhZG93cywgbWFzayB1dGlsaXRpZXMsIGlucHV0IGRldmljZSB2YXJpYW50cyBhbmQgZmFsbGJhY2tzIGZvciBvbGRlciBicm93c2Vycy4gSGVyZSBpcyB3aGF0IHNoaXBwZWQgYW5kIHRocmVlIHVzZXMgaW4gYSBWdWUgY29tcG9uZW50Lg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_lilac%2Cp_Ii9jb21tdW5pdHkvYmxvZy90YWlsd2luZC1jc3MtNC0xLXRleHQtc2hhZG93cy1hbmQtbWFza3Mi%2Cs_5yWWoTduQve1FFlv.png"
  "twitter:image:alt": "Tailwind CSS 4.1: text shadows, masks and safer utilities · vuejs.school"
---

> Index of every page on this site: [llms.txt](https://vuejs.school/llms.txt)

## What shipped in 4.1

The announcement lists these additions:

- Text shadows. Five sizes from `text-shadow-2xs` to `text-shadow-lg`, a colour with `text-shadow-sky-300`, and an opacity modifier, so `text-shadow-lg/50` is a large shadow at half opacity.
- Masks, which hide parts of an element using the transparency of a gradient or an image. Linear masks per side, such as `mask-t-from-50%` or `mask-l-from-50% mask-l-to-90%`, radial masks with `mask-radial-from-15%` and `mask-radial-at-right`, conic masks, and all of them composable on one element.
- Better rendering in older browsers, with fallbacks for colours, shadows, gradients and opacity modifiers (explained below).
- Fine-grained text wrapping with `wrap-break-word` and `wrap-anywhere`, which map to `overflow-wrap`.
- Coloured drop shadows: `drop-shadow-xl drop-shadow-cyan-500/50` on an SVG.
- Input device variants: `pointer-fine` for mice and trackpads, `pointer-coarse` for touchscreens, and `any-pointer-*` to match when any connected device qualifies.
- Baseline alignment to the last line of text with `items-baseline-last` and `self-baseline-last`.
- Safe alignment utilities such as `justify-center-safe`, which switch to start alignment when the content overflows.
- Source control in CSS: `@source not` to exclude a folder from class scanning, and `@source inline()` to force classes into the build, replacing the old `safelist` option.
- New variants: `details-content:`, `inverted-colors:`, `noscript:`, and `user-valid:` with `user-invalid:`.

The [GitHub release notes](https://github.com/tailwindlabs/tailwindcss/releases/tag/v4.1.0) add a few more: opacity modifiers on `shadow-*` and `inset-shadow-*`, `node_modules` ignored by default when scanning for classes, and the two-word position utilities renamed, so `bg-left-top` is deprecated in favour of `bg-top-left` and `object-left-top` in favour of `object-top-left`. The old names still work.

## Use three new utilities in a Vue component

This course card uses a text shadow, a mask and a pointer variant, all new in 4.1. The rest of its classes are ordinary Tailwind.

*components/CourseCard.vue*

```
<script setup lang="ts">
defineProps<{
  title: string
  tags: string[]
}>()
</script>

<template>
  <article class="rounded-2xl bg-slate-900 p-6 text-white">
    <h2 class="text-2xl font-semibold text-shadow-md text-shadow-black/40">
      {{ title }}
    </h2>

    <ul class="mt-4 flex gap-2 overflow-x-auto mask-r-from-80%">
      <li
        v-for="tag in tags"
        :key="tag"
        class="shrink-0 rounded-full bg-slate-700 px-3 py-1 text-sm"
      >
        {{ tag }}
      </li>
    </ul>

    <button
      type="button"
      class="mt-6 rounded-lg bg-sky-500 px-4 py-2 pointer-coarse:px-6 pointer-coarse:py-3"
    >
      Start lesson
    </button>
  </article>
</template>
```

`text-shadow-md text-shadow-black/40` lifts the heading off the dark background with a medium shadow in black at 40% opacity. Before 4.1 this needed a `text-shadow` declaration in a style block or an `@utility` of your own.

`mask-r-from-80%` fades the right edge of the tag list to transparent, starting 80% of the way across. Because the list scrolls sideways, the fade hints that more tags sit past the edge. The class does the job of `mask-image: linear-gradient(to right, black 80%, transparent)` in plain CSS.

`pointer-coarse:px-6 pointer-coarse:py-3` makes the button bigger on touchscreens only. A finger is less precise than a mouse, and this variant gives fingers a larger target without making every desktop button oversized. It compiles to `@media (pointer: coarse)`, which checks only the primary pointing device, so a touchscreen laptop with a mouse usually counts as `fine`. Use `any-pointer-coarse:` to match whenever any connected device is coarse.

## Show form errors only after the user interacts

Style a required field with `invalid:border-red-500` and it turns red as soon as the page loads, before anyone has typed in it. The new `user-invalid:` and `user-valid:` variants match only after the user has interacted with the control. This email field uses them for its border (`defineModel()` lets a parent bind it with `v-model`):

*components/EmailField.vue*

```
<script setup lang="ts">
const email = defineModel<string>({ default: '' })
</script>

<template>
  <label class="block">
    <span class="text-sm">Email</span>
    <input
      v-model="email"
      type="email"
      required
      class="mt-1 block w-full rounded-md border border-slate-300 px-3 py-2 user-invalid:border-red-500 user-valid:border-green-500"
    >
  </label>
</template>
```

MDN's compatibility data lists `:user-invalid` and `:user-valid` from Chrome 119, Safari 16.5 and Firefox 88. In an older browser the rule is skipped and the border stays `border-slate-300`, so the field still works without the colour hint.

## What the fallbacks fix in older browsers

Tailwind v4 builds on three newer CSS features: `@property`, which registers typed custom properties and drives shadows, transforms and gradients; `color-mix()`, which produces the opacity modifiers; and `oklch` colours for the default palette. In browsers without them, 4.0 degraded badly. The announcement says even colours and shadows might not render at all on an old iPhone or iPad stuck on Safari 15. It shows the same page in Safari 15.5 on 4.0 and 4.1, and on 4.0 several background gradients are missing.

The announcement lists four fallbacks in 4.1:

- Colours defined in `oklab` now render in older Safari.
- Features built on `@property`, such as shadows, transforms and gradients, now work in older Safari and Firefox.
- Colours with an opacity modifier get an inlined fallback.
- Gradients with an explicit interpolation method fall back to the browser's default.

Tailwind writes these fallbacks into the CSS it generates, so they arrive with the upgrade and need no change to your Vite config. The announcement's promise is modest: pages render and stay usable in older browsers, though in some cases a shadow colour may differ.

The release matters most if your analytics show visitors on an older iPad, or on a locked-down Firefox ESR older than 128. Check that share before you decide how much to test. The compatibility page also lists bleeding-edge features with limited support, such as `@starting-style` and `field-sizing: content`: avoid those utilities where older browsers matter.

## Keep overflowing content reachable

The "safer utilities" in the title are two sets that stop content wider than its box from getting lost.

`justify-center-safe` fixes the centred flex row that overflows, and there is a `-safe` version for every alignment property. With `justify-center`, content wider than its container overflows on both sides, and the left part cannot be scrolled into view. With `justify-center-safe` the browser switches to start alignment as soon as the content overflows, so it overflows only to the right and stays reachable. A toolbar of chips on a narrow phone is the usual victim.

`wrap-break-word` breaks a long word or URL that would otherwise push past its box. Inside a flex container it may not, and the announcement suggests `wrap-anywhere` there. The difference is that `wrap-anywhere` also allows those breaks when the browser works out the item's intrinsic (content-based) size, so the item can shrink without the `min-width: 0` that flex children usually need.

## Choose which files Tailwind scans

Two new forms of the `@source` directive go in your CSS entry file, after the import. Their paths are relative to that file:

*src/assets/main.css*

```
@import "tailwindcss";
@source not "../legacy";
@source inline("{hover:,}bg-red-{50,{100..900..100},950}");
```

`@source not` keeps a folder out of class detection. Use it for an old part of the project that still ships its own stylesheet, or for a large folder that only slows the scan down.

`@source inline()` generates classes that never appear in your source, for example colours chosen at runtime from data. It supports brace expansion, so the line above produces `bg-red-50` through `bg-red-950` plus their `hover:` variants. It replaces the `safelist` array from the v3 JavaScript config. There is also `@source not inline("container")` to exclude a class even if it is detected.

## Upgrade and check your oldest browser

In a Vue project that uses the Vite plugin, update both packages:

```
npm install tailwindcss@latest @tailwindcss/vite@latest
```

If you set Tailwind up through PostCSS, install `@tailwindcss/postcss@latest` instead of `@tailwindcss/vite@latest`. Your CSS entry still starts with `@import "tailwindcss"`, and your `@theme` block stays as it is. Restart `npm run dev` after the install.

Then check two things:

1. Open the pages that use gradients and shadows in the oldest browser you support, and confirm they render.
2. If a component library in `node_modules` got its classes from Tailwind's automatic scan, add a line for it to `src/assets/main.css`, such as `@source "../../node_modules/that-library";`. Tailwind 4.0 scanned `node_modules` unless your `.gitignore` listed it; 4.1 skips it by default, and an explicit `@source` overrides that.

**Check it stuck**3 questions from the Tailwind CSS with Vue quiz.

[**Practice now**](https://vuejs.school/learn/styling/tailwind-css-with-vue?tab=quiz)

**Was this helpful?**

## **Keep reading**

[Tutorial · 7 min read**Component libraries in 2026: Nuxt UI, PrimeVue, Vuetify and when to use none**](https://vuejs.school/community/blog/component-libraries-in-2026) [Tutorial · 7 min read**Accessible components: focus, keyboard and ARIA**](https://vuejs.school/community/blog/accessible-components-focus-keyboard-and-aria)

```json
{"@context":"https://schema.org","@graph":[{"@id":"https://vuejs.school/#website","@type":"WebSite","description":"A free AI-instructed school for Vue.js. Short tutorials, runnable code and quizzes that adapt to you.","name":"vuejs.school","url":"https://vuejs.school/","publisher":{"@id":"https://vuejs.school/#identity"}},{"@id":"https://vuejs.school/community/blog/tailwind-css-4-1-text-shadows-and-masks#webpage","@type":"WebPage","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2025-04-03","description":"Tailwind CSS 4.1 adds text shadows, mask utilities, input device variants and fallbacks for older browsers. Here is what shipped and three uses in a Vue component.","name":"Tailwind CSS 4.1: text shadows, masks and safer utilities","url":"https://vuejs.school/community/blog/tailwind-css-4-1-text-shadows-and-masks","breadcrumb":{"@id":"https://vuejs.school/community/blog/tailwind-css-4-1-text-shadows-and-masks#breadcrumb"},"isPartOf":{"@id":"https://vuejs.school/#website"},"potentialAction":[{"@type":"ReadAction","target":["https://vuejs.school/community/blog/tailwind-css-4-1-text-shadows-and-masks"]}],"primaryImageOfPage":{"@id":"https://vuejs.school/#logo"}},{"@id":"https://vuejs.school/#identity","@type":"Organization","description":"A free AI-instructed school for Vue.js. Short tutorials, runnable code and quizzes that adapt to you.","name":"vuejs.school","url":"https://vuejs.school","logo":{"@id":"https://vuejs.school/#logo"}},{"@id":"https://vuejs.school/community/blog/tailwind-css-4-1-text-shadows-and-masks#article","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2025-04-03","description":"Tailwind CSS 4.1 adds text shadows, mask utilities, input device variants and fallbacks for older browsers. Here is what shipped and three uses in a Vue component.","headline":"Tailwind CSS 4.1: text shadows, masks and safer utilities","inLanguage":"en","isAccessibleForFree":true,"thumbnailUrl":"https://vuejs.school/_og/d/a_~VGFpbHdpbmQgQ1NTIDQuMTogdGV4dCBzaGFkb3dzLCBtYXNrcyBhbmQgc2FmZXIgdXRpbGl0aWVzIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~VGFpbHdpbmQgQ1NTIDQuMTogdGV4dCBzaGFkb3dzLCBtYXNrcyBhbmQgc2FmZXIgdXRpbGl0aWVz%2Cdescription_~VGFpbHdpbmQgQ1NTIDQuMSBhZGRzIHRleHQgc2hhZG93cywgbWFzayB1dGlsaXRpZXMsIGlucHV0IGRldmljZSB2YXJpYW50cyBhbmQgZmFsbGJhY2tzIGZvciBvbGRlciBicm93c2Vycy4gSGVyZSBpcyB3aGF0IHNoaXBwZWQgYW5kIHRocmVlIHVzZXMgaW4gYSBWdWUgY29tcG9uZW50Lg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_lilac%2Cp_Ii9jb21tdW5pdHkvYmxvZy90YWlsd2luZC1jc3MtNC0xLXRleHQtc2hhZG93cy1hbmQtbWFza3Mi%2Cs_5yWWoTduQve1FFlv.png","@type":["Article","BlogPosting"],"articleSection":["Release notes"],"author":{"@id":"https://vuejs.school/#identity"},"image":{"@id":"https://vuejs.school/#/schema/image/1"},"isPartOf":{"@id":"https://vuejs.school/community/blog/tailwind-css-4-1-text-shadows-and-masks#webpage"},"mainEntityOfPage":{"@id":"https://vuejs.school/community/blog/tailwind-css-4-1-text-shadows-and-masks#webpage"},"publisher":{"@id":"https://vuejs.school/#identity"}},{"@id":"https://vuejs.school/community/blog/tailwind-css-4-1-text-shadows-and-masks#breadcrumb","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","name":"Blog","item":"https://vuejs.school/community/blog","position":1},{"@type":"ListItem","name":"Tailwind CSS 4.1: text shadows, masks and safer utilities","position":2}]},{"@id":"https://vuejs.school/#logo","@type":"ImageObject","caption":"vuejs.school","contentUrl":"https://vuejs.school/apple-touch-icon.png","inLanguage":"en","url":"https://vuejs.school/apple-touch-icon.png"},{"@id":"https://vuejs.school/#/schema/image/1","@type":"ImageObject","contentUrl":"https://vuejs.school/_og/d/a_~VGFpbHdpbmQgQ1NTIDQuMTogdGV4dCBzaGFkb3dzLCBtYXNrcyBhbmQgc2FmZXIgdXRpbGl0aWVzIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~VGFpbHdpbmQgQ1NTIDQuMTogdGV4dCBzaGFkb3dzLCBtYXNrcyBhbmQgc2FmZXIgdXRpbGl0aWVz%2Cdescription_~VGFpbHdpbmQgQ1NTIDQuMSBhZGRzIHRleHQgc2hhZG93cywgbWFzayB1dGlsaXRpZXMsIGlucHV0IGRldmljZSB2YXJpYW50cyBhbmQgZmFsbGJhY2tzIGZvciBvbGRlciBicm93c2Vycy4gSGVyZSBpcyB3aGF0IHNoaXBwZWQgYW5kIHRocmVlIHVzZXMgaW4gYSBWdWUgY29tcG9uZW50Lg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_lilac%2Cp_Ii9jb21tdW5pdHkvYmxvZy90YWlsd2luZC1jc3MtNC0xLXRleHQtc2hhZG93cy1hbmQtbWFza3Mi%2Cs_5yWWoTduQve1FFlv.png","inLanguage":"en","url":"https://vuejs.school/_og/d/a_~VGFpbHdpbmQgQ1NTIDQuMTogdGV4dCBzaGFkb3dzLCBtYXNrcyBhbmQgc2FmZXIgdXRpbGl0aWVzIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~VGFpbHdpbmQgQ1NTIDQuMTogdGV4dCBzaGFkb3dzLCBtYXNrcyBhbmQgc2FmZXIgdXRpbGl0aWVz%2Cdescription_~VGFpbHdpbmQgQ1NTIDQuMSBhZGRzIHRleHQgc2hhZG93cywgbWFzayB1dGlsaXRpZXMsIGlucHV0IGRldmljZSB2YXJpYW50cyBhbmQgZmFsbGJhY2tzIGZvciBvbGRlciBicm93c2Vycy4gSGVyZSBpcyB3aGF0IHNoaXBwZWQgYW5kIHRocmVlIHVzZXMgaW4gYSBWdWUgY29tcG9uZW50Lg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_lilac%2Cp_Ii9jb21tdW5pdHkvYmxvZy90YWlsd2luZC1jc3MtNC0xLXRleHQtc2hhZG93cy1hbmQtbWFza3Mi%2Cs_5yWWoTduQve1FFlv.png"}]}
```