Skip to content

Commit 9ab4732

Browse files
Add support for addBase in plugins (#14172)
This PR adds support for `addBase` in JS plugins which adds styles into the CSS base layer using `@layer base`. This exists for backwards compatibility with v3 but is not something we will encourage people to use going forward — in v4 it's better to just write these styles in a CSS file. In v3, `@layer base` was something we compiled away and was only used for determining where to add some styles in the final CSS, but in v4 we are using native CSS layers. This means that `addBase` in v4 expects you to have a _real_ `@layer base` in your final CSS, which you will have as long as you are using `@import "tailwindcss"` to add Tailwind to your project. Now something like this works: ```js function ({ addBase }) { addBase({ 'h1': { fontSize: '2em' }, 'h2': { fontSize: '1.5em' }, }) } ``` Which will emit the following CSS: ```css @layer base { h1 { font-size: 2em; } h2 { font-size: 1.5em; } } ``` The only limitation compared to v3 is that there is no way for you to wrap these styles in another custom layer. In v3 you could do this: ```css @layer my-base { @tailwind base; } ``` …and then anything you added with `addBase` would end up exactly where `@tailwind base` was in your source CSS. But in v4 there is no `@tailwind base`, so there's no way to wrap these styles in `@layer my-base` like in the example above. All base styles added by plugins are simply appended to the end of the stylesheet but wrapped in `@layer base` so they behave as if they are co-located with other base styles. Odds of this impacting anyone are extremely low, but if it proves to be an actual issue I think we could output these styles at the location of an optional `@tailwind base` rule if we detect it exists.
1 parent 8cace0d commit 9ab4732

File tree

4 files changed

+51
-4
lines changed

4 files changed

+51
-4
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
- Nothing yet!
10+
### Added
11+
12+
- Add support for `addBase` plugins using the `@plugin` directive ([#14172](https://github.com/tailwindlabs/tailwindcss/pull/14172))
1113

1214
## [4.0.0-alpha.19] - 2024-08-09
1315

packages/tailwindcss/src/index.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2074,3 +2074,43 @@ describe('@variant', () => {
20742074
`)
20752075
})
20762076
})
2077+
2078+
test('addBase', async () => {
2079+
let { build } = await compile(
2080+
css`
2081+
@plugin "my-plugin";
2082+
@layer base, utilities;
2083+
@layer utilities {
2084+
@tailwind utilities;
2085+
}
2086+
`,
2087+
2088+
{
2089+
loadPlugin: async () => {
2090+
return ({ addBase }) => {
2091+
addBase({
2092+
body: {
2093+
'font-feature-settings': '"tnum"',
2094+
},
2095+
})
2096+
}
2097+
},
2098+
},
2099+
)
2100+
2101+
let compiled = build(['underline'])
2102+
2103+
expect(optimizeCss(compiled).trim()).toMatchInlineSnapshot(`
2104+
"@layer base {
2105+
body {
2106+
font-feature-settings: "tnum";
2107+
}
2108+
}
2109+
2110+
@layer utilities {
2111+
.underline {
2112+
text-decoration-line: underline;
2113+
}
2114+
}"
2115+
`)
2116+
})

packages/tailwindcss/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ async function parseCss(css: string, { loadPlugin = throwOnPlugin }: CompileOpti
281281
customUtility(designSystem)
282282
}
283283

284-
let pluginApi = buildPluginApi(designSystem)
284+
let pluginApi = buildPluginApi(designSystem, ast)
285285

286286
await Promise.all(pluginLoaders.map((loader) => loader.then((plugin) => plugin(pluginApi))))
287287

packages/tailwindcss/src/plugin-api.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { substituteAtApply } from './apply'
2-
import { objectToAst, rule, type CssInJs } from './ast'
2+
import { objectToAst, rule, type AstNode, type CssInJs } from './ast'
33
import type { DesignSystem } from './design-system'
44
import { withAlpha, withNegative } from './utilities'
55
import { inferDataType } from './utils/infer-data-type'
66

77
export type PluginAPI = {
8+
addBase(base: CssInJs): void
89
addVariant(name: string, variant: string | string[] | CssInJs): void
910
addUtilities(utilities: Record<string, CssInJs>, options?: {}): void
1011
matchUtilities(
@@ -20,8 +21,12 @@ export type PluginAPI = {
2021

2122
const IS_VALID_UTILITY_NAME = /^[a-z][a-zA-Z0-9/%._-]*$/
2223

23-
export function buildPluginApi(designSystem: DesignSystem): PluginAPI {
24+
export function buildPluginApi(designSystem: DesignSystem, ast: AstNode[]): PluginAPI {
2425
return {
26+
addBase(css) {
27+
ast.push(rule('@layer base', objectToAst(css)))
28+
},
29+
2530
addVariant(name, variant) {
2631
// Single selector
2732
if (typeof variant === 'string') {

0 commit comments

Comments
 (0)