Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Support keyframes in JS theme config
  • Loading branch information
philipp-spiess committed Oct 4, 2024
commit 39ae98d41218b2b4306849e1d6e528682dffc21b
2 changes: 2 additions & 0 deletions packages/tailwindcss/src/compat/apply-compat-hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { withAlpha } from '../utilities'
import { segment } from '../utils/segment'
import { toKeyPath } from '../utils/to-key-path'
import { applyConfigToTheme } from './apply-config-to-theme'
import { applyKeyframesToAst } from './apply-keyframes-to-ast'
import { createCompatConfig } from './config/create-compat-config'
import { resolveConfig } from './config/resolve-config'
import type { UserConfig } from './config/types'
Expand Down Expand Up @@ -206,6 +207,7 @@ export async function applyCompatibilityHooks({
// config would otherwise expand into namespaces like `background-color` which
// core utilities already read from.
applyConfigToTheme(designSystem, resolvedUserConfig)
applyKeyframesToAst(ast, resolvedUserConfig)

registerThemeVariantOverrides(resolvedUserConfig, designSystem)
registerScreensConfig(resolvedUserConfig, designSystem)
Expand Down
54 changes: 54 additions & 0 deletions packages/tailwindcss/src/compat/apply-keyframes-to-ast.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { expect, test } from 'vitest'
import { toCss, type AstNode } from '../ast'
import { buildDesignSystem } from '../design-system'
import { Theme } from '../theme'
import { applyKeyframesToAst } from './apply-keyframes-to-ast'
import { resolveConfig } from './config/resolve-config'

test('Config values can be merged into the theme', () => {
let theme = new Theme()
let design = buildDesignSystem(theme)

let ast: AstNode[] = []

let resolvedUserConfig = resolveConfig(design, [
{
config: {
theme: {
keyframes: {
'fade-in': {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
'fade-out': {
'0%': { opacity: '1' },
'100%': { opacity: '0' },
},
},
},
},
base: '/root',
},
])
applyKeyframesToAst(ast, resolvedUserConfig)

expect(toCss(ast)).toMatchInlineSnapshot(`
"@keyframes fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fade-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
"
`)
})
11 changes: 11 additions & 0 deletions packages/tailwindcss/src/compat/apply-keyframes-to-ast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { rule, type AstNode } from '../ast'
import type { ResolvedConfig } from './config/types'
import { objectToAst } from './plugin-api'

export function applyKeyframesToAst(ast: AstNode[], { theme }: ResolvedConfig) {
if ('keyframes' in theme) {
for (let [name, keyframe] of Object.entries(theme.keyframes)) {
ast.push(rule(`@keyframes ${name}`, objectToAst(keyframe as any)))
}
}
}
12 changes: 12 additions & 0 deletions packages/tailwindcss/src/compat/plugin-api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ describe('theme', async () => {
}
}
}
@keyframes enter {
from {
opacity: var(--tw-enter-opacity, 1);
transform: translate3d(var(--tw-enter-translate-x, 0), var(--tw-enter-translate-y, 0), 0) scale3d(var(--tw-enter-scale, 1), var(--tw-enter-scale, 1), var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0));
}
}
@keyframes exit {
to {
opacity: var(--tw-exit-opacity, 1);
transform: translate3d(var(--tw-exit-translate-x, 0), var(--tw-exit-translate-y, 0), 0) scale3d(var(--tw-exit-scale, 1), var(--tw-exit-scale, 1), var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0));
}
}
Comment on lines +70 to +81
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh oh wait what.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting, in v3, plugins require an addBase to add the keyframes but if it was part of the JS config theme, it would be added automatically. That's lovely:
https://play.tailwindcss.com/YQLQchsWWW?file=config

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In v3 the way this worked was keyframes were not emitted until used. So you had to do @keyframes …: theme('keyframes.whatever`) in your utility function. And we handle that already.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we do in addUtilities at least. I don't think we do in matchUtilities which we should I think.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or at least I'm like 95% sure it works that way. Maybe we just de-duped them…

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh gotch you're right they're only added when used! If you use addBase and use it with an animate-* variable, it is emitted twice in v3 (at the end of the stylesheet).

Screenshot 2024-10-04 at 12 56 42

I guess it's fine if we always emit keyframes for compat now (we do this for v4 keyframes too!). However unsure if we should then guard against this case. Maybe it's ok if the keyframes are emitted twice here since it does no harm? WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah it's probably fine — would be nice to dedupe them but that can be an optimization done later. They should still be de-duped when minifying with lightingcss.

"
`)
})
Expand Down
2 changes: 1 addition & 1 deletion packages/tailwindcss/src/compat/plugin-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ export function buildPluginApi(

export type CssInJs = { [key: string]: string | string[] | CssInJs | CssInJs[] }

function objectToAst(rules: CssInJs | CssInJs[]): AstNode[] {
export function objectToAst(rules: CssInJs | CssInJs[]): AstNode[] {
let ast: AstNode[] = []

rules = Array.isArray(rules) ? rules : [rules]
Expand Down