Skip to content

Commit 84c72ca

Browse files
Add text-shadow-* utilities
1 parent 3dcd615 commit 84c72ca

File tree

7 files changed

+332
-4
lines changed

7 files changed

+332
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- _Experimental_: Add `wrap-anywhere`, `wrap-break-word`, and `wrap-normal` utilities ([#12128](https://github.com/tailwindlabs/tailwindcss/pull/12128))
2121
- _Experimental_: Add `@source inline(…)` ([#17147](https://github.com/tailwindlabs/tailwindcss/pull/17147))
2222
- _Experimental_: Add `@source not` ([#17255](https://github.com/tailwindlabs/tailwindcss/pull/17255))
23+
- _Experimental_: Add `text-shadow-*` utilities
2324

2425
### Fixed
2526

packages/tailwindcss/src/__snapshots__/intellisense.test.ts.snap

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7756,6 +7756,75 @@ exports[`getClassList 1`] = `
77567756
"text-nowrap",
77577757
"text-pretty",
77587758
"text-right",
7759+
"text-shadow",
7760+
"text-shadow-current",
7761+
"text-shadow-current/0",
7762+
"text-shadow-current/5",
7763+
"text-shadow-current/10",
7764+
"text-shadow-current/15",
7765+
"text-shadow-current/20",
7766+
"text-shadow-current/25",
7767+
"text-shadow-current/30",
7768+
"text-shadow-current/35",
7769+
"text-shadow-current/40",
7770+
"text-shadow-current/45",
7771+
"text-shadow-current/50",
7772+
"text-shadow-current/55",
7773+
"text-shadow-current/60",
7774+
"text-shadow-current/65",
7775+
"text-shadow-current/70",
7776+
"text-shadow-current/75",
7777+
"text-shadow-current/80",
7778+
"text-shadow-current/85",
7779+
"text-shadow-current/90",
7780+
"text-shadow-current/95",
7781+
"text-shadow-current/100",
7782+
"text-shadow-inherit",
7783+
"text-shadow-inherit/0",
7784+
"text-shadow-inherit/5",
7785+
"text-shadow-inherit/10",
7786+
"text-shadow-inherit/15",
7787+
"text-shadow-inherit/20",
7788+
"text-shadow-inherit/25",
7789+
"text-shadow-inherit/30",
7790+
"text-shadow-inherit/35",
7791+
"text-shadow-inherit/40",
7792+
"text-shadow-inherit/45",
7793+
"text-shadow-inherit/50",
7794+
"text-shadow-inherit/55",
7795+
"text-shadow-inherit/60",
7796+
"text-shadow-inherit/65",
7797+
"text-shadow-inherit/70",
7798+
"text-shadow-inherit/75",
7799+
"text-shadow-inherit/80",
7800+
"text-shadow-inherit/85",
7801+
"text-shadow-inherit/90",
7802+
"text-shadow-inherit/95",
7803+
"text-shadow-inherit/100",
7804+
"text-shadow-initial",
7805+
"text-shadow-none",
7806+
"text-shadow-transparent",
7807+
"text-shadow-transparent/0",
7808+
"text-shadow-transparent/5",
7809+
"text-shadow-transparent/10",
7810+
"text-shadow-transparent/15",
7811+
"text-shadow-transparent/20",
7812+
"text-shadow-transparent/25",
7813+
"text-shadow-transparent/30",
7814+
"text-shadow-transparent/35",
7815+
"text-shadow-transparent/40",
7816+
"text-shadow-transparent/45",
7817+
"text-shadow-transparent/50",
7818+
"text-shadow-transparent/55",
7819+
"text-shadow-transparent/60",
7820+
"text-shadow-transparent/65",
7821+
"text-shadow-transparent/70",
7822+
"text-shadow-transparent/75",
7823+
"text-shadow-transparent/80",
7824+
"text-shadow-transparent/85",
7825+
"text-shadow-transparent/90",
7826+
"text-shadow-transparent/95",
7827+
"text-shadow-transparent/100",
77597828
"text-start",
77607829
"text-transparent",
77617830
"text-transparent/0",

packages/tailwindcss/src/feature-flags.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ export const enableSafeAlignment = process.env.FEATURES_ENV !== 'stable'
66
export const enableScripting = process.env.FEATURES_ENV !== 'stable'
77
export const enableSourceInline = process.env.FEATURES_ENV !== 'stable'
88
export const enableSourceNot = process.env.FEATURES_ENV !== 'stable'
9+
export const enableTextShadows = process.env.FEATURES_ENV !== 'stable'
910
export const enableUserValid = process.env.FEATURES_ENV !== 'stable'
1011
export const enableWrapAnywhere = process.env.FEATURES_ENV !== 'stable'

packages/tailwindcss/src/theme.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ const ignoredThemeKeyMap = new Map([
2121
'--text',
2222
[
2323
'--text-color',
24-
'--text-underline-offset',
25-
'--text-indent',
26-
'--text-decoration-thickness',
2724
'--text-decoration-color',
25+
'--text-decoration-thickness',
26+
'--text-indent',
27+
'--text-shadow',
28+
'--text-underline-offset',
2829
],
2930
],
3031
])

packages/tailwindcss/src/utilities.test.ts

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15328,6 +15328,160 @@ test('text', async () => {
1532815328
).toEqual('')
1532915329
})
1533015330

15331+
test('text-shadow', async () => {
15332+
expect(
15333+
await compileCss(
15334+
css`
15335+
@theme {
15336+
--color-red-500: #ef4444;
15337+
--text-shadow-sm: 0px 1px 2px rgb(0 0 0 / 0.1);
15338+
--text-shadow-xl: 0px 2px 4px rgb(0 0 0 / 0.15), 0px 2px 6px rgb(0 0 0 / 0.15);
15339+
}
15340+
@tailwind utilities;
15341+
`,
15342+
[
15343+
// Shadows
15344+
'text-shadow-sm',
15345+
'text-shadow-xl',
15346+
'text-shadow-none',
15347+
'text-shadow-[12px_12px_#0088cc]',
15348+
'text-shadow-[10px_10px]',
15349+
'text-shadow-[var(--value)]',
15350+
'text-shadow-[shadow:var(--value)]',
15351+
15352+
// Colors
15353+
'text-shadow-red-500',
15354+
'text-shadow-red-500/50',
15355+
'text-shadow-red-500/2.25',
15356+
'text-shadow-red-500/2.5',
15357+
'text-shadow-red-500/2.75',
15358+
'text-shadow-red-500/[0.5]',
15359+
'text-shadow-red-500/[50%]',
15360+
'text-shadow-current',
15361+
'text-shadow-current/50',
15362+
'text-shadow-current/[0.5]',
15363+
'text-shadow-current/[50%]',
15364+
'text-shadow-inherit',
15365+
'text-shadow-transparent',
15366+
'text-shadow-[#0088cc]',
15367+
'text-shadow-[#0088cc]/50',
15368+
'text-shadow-[#0088cc]/[0.5]',
15369+
'text-shadow-[#0088cc]/[50%]',
15370+
'text-shadow-[color:var(--value)]',
15371+
'text-shadow-[color:var(--value)]/50',
15372+
'text-shadow-[color:var(--value)]/[0.5]',
15373+
'text-shadow-[color:var(--value)]/[50%]',
15374+
],
15375+
),
15376+
).toMatchInlineSnapshot(`
15377+
":root, :host {
15378+
--color-red-500: #ef4444;
15379+
}
15380+
15381+
.text-shadow-\\[\\#0088cc\\] {
15382+
--tw-text-shadow-color: #08c;
15383+
}
15384+
15385+
.text-shadow-\\[\\#0088cc\\]\\/50, .text-shadow-\\[\\#0088cc\\]\\/\\[0\\.5\\], .text-shadow-\\[\\#0088cc\\]\\/\\[50\\%\\] {
15386+
--tw-text-shadow-color: oklab(59.9824% -.06725 -.12414 / .5);
15387+
}
15388+
15389+
.text-shadow-\\[10px_10px\\] {
15390+
text-shadow: 10px 10px var(--tw-text-shadow-color, currentcolor);
15391+
}
15392+
15393+
.text-shadow-\\[12px_12px_\\#0088cc\\] {
15394+
text-shadow: 12px 12px var(--tw-text-shadow-color, #08c);
15395+
}
15396+
15397+
.text-shadow-\\[color\\:var\\(--value\\)\\] {
15398+
--tw-text-shadow-color: var(--value);
15399+
}
15400+
15401+
.text-shadow-\\[color\\:var\\(--value\\)\\]\\/50, .text-shadow-\\[color\\:var\\(--value\\)\\]\\/\\[0\\.5\\], .text-shadow-\\[color\\:var\\(--value\\)\\]\\/\\[50\\%\\] {
15402+
--tw-text-shadow-color: color-mix(in oklab, var(--value) 50%, transparent);
15403+
}
15404+
15405+
.text-shadow-\\[shadow\\:var\\(--value\\)\\], .text-shadow-\\[var\\(--value\\)\\] {
15406+
text-shadow: var(--value);
15407+
}
15408+
15409+
.text-shadow-current {
15410+
--tw-text-shadow-color: currentColor;
15411+
}
15412+
15413+
.text-shadow-current\\/50, .text-shadow-current\\/\\[0\\.5\\], .text-shadow-current\\/\\[50\\%\\] {
15414+
--tw-text-shadow-color: color-mix(in oklab, currentColor 50%, transparent);
15415+
}
15416+
15417+
.text-shadow-inherit {
15418+
--tw-text-shadow-color: inherit;
15419+
}
15420+
15421+
.text-shadow-none {
15422+
text-shadow: none;
15423+
}
15424+
15425+
.text-shadow-red-500 {
15426+
--tw-text-shadow-color: var(--color-red-500);
15427+
}
15428+
15429+
.text-shadow-red-500\\/2\\.5 {
15430+
--tw-text-shadow-color: color-mix(in oklab, var(--color-red-500) 2.5%, transparent);
15431+
}
15432+
15433+
.text-shadow-red-500\\/2\\.25 {
15434+
--tw-text-shadow-color: color-mix(in oklab, var(--color-red-500) 2.25%, transparent);
15435+
}
15436+
15437+
.text-shadow-red-500\\/2\\.75 {
15438+
--tw-text-shadow-color: color-mix(in oklab, var(--color-red-500) 2.75%, transparent);
15439+
}
15440+
15441+
.text-shadow-red-500\\/50, .text-shadow-red-500\\/\\[0\\.5\\], .text-shadow-red-500\\/\\[50\\%\\] {
15442+
--tw-text-shadow-color: color-mix(in oklab, var(--color-red-500) 50%, transparent);
15443+
}
15444+
15445+
.text-shadow-sm {
15446+
text-shadow: 0px 1px 2px var(--tw-text-shadow-color, #0000001a);
15447+
}
15448+
15449+
.text-shadow-transparent {
15450+
--tw-text-shadow-color: transparent;
15451+
}
15452+
15453+
.text-shadow-xl {
15454+
text-shadow: 0px 2px 4px var(--tw-text-shadow-color, #00000026), 0px 2px 6px var(--tw-text-shadow-color, #00000026);
15455+
}
15456+
15457+
@property --tw-text-shadow-color {
15458+
syntax: "*";
15459+
inherits: false
15460+
}"
15461+
`)
15462+
expect(
15463+
await run([
15464+
'-shadow-xl',
15465+
'-shadow-none',
15466+
'-shadow-red-500',
15467+
'-shadow-red-500/50',
15468+
'-shadow-red-500/[0.5]',
15469+
'-shadow-red-500/[50%]',
15470+
'-shadow-current',
15471+
'-shadow-current/50',
15472+
'-shadow-current/[0.5]',
15473+
'-shadow-current/[50%]',
15474+
'-shadow-inherit',
15475+
'-shadow-transparent',
15476+
'-shadow-[#0088cc]',
15477+
'-shadow-[#0088cc]/50',
15478+
'-shadow-[#0088cc]/[0.5]',
15479+
'-shadow-[#0088cc]/[50%]',
15480+
'-shadow-[var(--value)]',
15481+
]),
15482+
).toEqual('')
15483+
})
15484+
1533115485
test('shadow', async () => {
1533215486
expect(
1533315487
await compileCss(

packages/tailwindcss/src/utilities.ts

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ import {
1111
} from './ast'
1212
import type { Candidate, CandidateModifier, NamedUtilityValue } from './candidate'
1313
import type { DesignSystem } from './design-system'
14-
import { enableBaselineLast, enableSafeAlignment, enableWrapAnywhere } from './feature-flags'
14+
import {
15+
enableBaselineLast,
16+
enableSafeAlignment,
17+
enableTextShadows,
18+
enableWrapAnywhere,
19+
} from './feature-flags'
1520
import type { Theme, ThemeKey } from './theme'
1621
import { compareBreakpoints } from './utils/compare-breakpoints'
1722
import { DefaultMap } from './utils/default-map'
@@ -4242,6 +4247,97 @@ export function createUtilities(theme: Theme) {
42424247
},
42434248
])
42444249

4250+
if (enableTextShadows) {
4251+
let textShadowProperties = () => {
4252+
return atRoot([property('--tw-text-shadow-color')])
4253+
}
4254+
4255+
staticUtility('text-shadow-initial', [
4256+
textShadowProperties,
4257+
['--tw-text-shadow-color', 'initial'],
4258+
])
4259+
4260+
utilities.functional('text-shadow', (candidate) => {
4261+
if (!candidate.value) {
4262+
let value = theme.get(['--text-shadow'])
4263+
if (value === null) return
4264+
4265+
return [
4266+
textShadowProperties(),
4267+
decl(
4268+
'text-shadow',
4269+
replaceShadowColors(value, (color) => `var(--tw-text-shadow-color, ${color})`),
4270+
),
4271+
]
4272+
}
4273+
4274+
if (candidate.value.kind === 'arbitrary') {
4275+
let value: string | null = candidate.value.value
4276+
let type = candidate.value.dataType ?? inferDataType(value, ['color'])
4277+
4278+
switch (type) {
4279+
case 'color': {
4280+
value = asColor(value, candidate.modifier, theme)
4281+
if (value === null) return
4282+
4283+
return [textShadowProperties(), decl('--tw-text-shadow-color', value)]
4284+
}
4285+
default: {
4286+
return [
4287+
textShadowProperties(),
4288+
decl(
4289+
'text-shadow',
4290+
replaceShadowColors(value, (color) => `var(--tw-text-shadow-color, ${color})`),
4291+
),
4292+
]
4293+
}
4294+
}
4295+
}
4296+
4297+
switch (candidate.value.value) {
4298+
case 'none':
4299+
if (candidate.modifier) return
4300+
return [textShadowProperties(), decl('text-shadow', 'none')]
4301+
}
4302+
4303+
// Shadow size
4304+
{
4305+
let value = theme.get([`--text-shadow-${candidate.value.value}`])
4306+
if (value) {
4307+
if (candidate.modifier) return
4308+
return [
4309+
textShadowProperties(),
4310+
decl(
4311+
'text-shadow',
4312+
replaceShadowColors(value, (color) => `var(--tw-text-shadow-color, ${color})`),
4313+
),
4314+
]
4315+
}
4316+
}
4317+
4318+
// Shadow color
4319+
{
4320+
let value = resolveThemeColor(candidate, theme, ['--text-shadow-color', '--color'])
4321+
if (value) {
4322+
return [textShadowProperties(), decl('--tw-text-shadow-color', value)]
4323+
}
4324+
}
4325+
})
4326+
4327+
suggest('text-shadow', () => [
4328+
{
4329+
values: ['current', 'inherit', 'transparent'],
4330+
valueThemeKeys: ['--text-shadow-color', '--color'],
4331+
modifiers: Array.from({ length: 21 }, (_, index) => `${index * 5}`),
4332+
},
4333+
{
4334+
values: ['none'],
4335+
valueThemeKeys: ['--text-shadow'],
4336+
hasDefaultValue: true,
4337+
},
4338+
])
4339+
}
4340+
42454341
{
42464342
let cssBoxShadowValue = [
42474343
'var(--tw-inset-shadow)',

packages/tailwindcss/theme.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,12 @@
374374
--drop-shadow-xl: 0 9px 7px rgb(0 0 0 / 0.1);
375375
--drop-shadow-2xl: 0 25px 25px rgb(0 0 0 / 0.15);
376376

377+
--text-shadow-xs: 0px 1px 0px rgb(0 0 0 / 0.1);
378+
--text-shadow-sm: 0px 1px 2px rgb(0 0 0 / 0.1);
379+
--text-shadow-md: 0px 1px 2px rgb(0 0 0 / 0.06), 0px 2px 2px rgb(0 0 0 / 0.06);
380+
--text-shadow-lg: 0px 1px 2px rgb(0 0 0 / 0.1), 0px 2px 4px rgb(0 0 0 / 0.1);
381+
--text-shadow-xl: 0px 2px 4px rgb(0 0 0 / 0.15), 0px 2px 6px rgb(0 0 0 / 0.15);
382+
377383
--ease-in: cubic-bezier(0.4, 0, 1, 1);
378384
--ease-out: cubic-bezier(0, 0, 0.2, 1);
379385
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);

0 commit comments

Comments
 (0)