From f00eab18dc0acc213774fda721a598fafe580623 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 19 Aug 2024 13:17:28 -0400 Subject: [PATCH 01/25] Fix export --- packages/tailwindcss/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tailwindcss/package.json b/packages/tailwindcss/package.json index 5bd5b7922325..a98b53d7c957 100644 --- a/packages/tailwindcss/package.json +++ b/packages/tailwindcss/package.json @@ -49,7 +49,7 @@ }, "./plugin": { "require": "./dist/plugin.js", - "import": "./src/plugin.mjs" + "import": "./dist/plugin.mjs" }, "./package.json": "./package.json", "./index.css": "./index.css", From 0baa1fc3bd5796ab2cfd3cdc1b3fce67b24b703b Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 19 Aug 2024 13:17:39 -0400 Subject: [PATCH 02/25] Add colors export --- packages/tailwindcss/package.json | 8 + packages/tailwindcss/src/compat/colors.cts | 1 + packages/tailwindcss/src/compat/colors.ts | 293 +++++++++++++++++++++ packages/tailwindcss/tsup.config.ts | 2 + 4 files changed, 304 insertions(+) create mode 100644 packages/tailwindcss/src/compat/colors.cts create mode 100644 packages/tailwindcss/src/compat/colors.ts diff --git a/packages/tailwindcss/package.json b/packages/tailwindcss/package.json index a98b53d7c957..0655dcb21e52 100644 --- a/packages/tailwindcss/package.json +++ b/packages/tailwindcss/package.json @@ -23,6 +23,10 @@ "require": "./dist/lib.js", "import": "./src/index.ts" }, + "./colors": { + "require": "./src/compat/colors.cts", + "import": "./src/compat/colors.ts" + }, "./plugin": { "require": "./src/plugin.cts", "import": "./src/plugin.ts" @@ -51,6 +55,10 @@ "require": "./dist/plugin.js", "import": "./dist/plugin.mjs" }, + "./colors": { + "require": "./dist/colors.js", + "import": "./dist/colors.mjs" + }, "./package.json": "./package.json", "./index.css": "./index.css", "./index": "./index.css", diff --git a/packages/tailwindcss/src/compat/colors.cts b/packages/tailwindcss/src/compat/colors.cts new file mode 100644 index 000000000000..34d90e1e965d --- /dev/null +++ b/packages/tailwindcss/src/compat/colors.cts @@ -0,0 +1 @@ +module.exports = require('./colors.ts').default diff --git a/packages/tailwindcss/src/compat/colors.ts b/packages/tailwindcss/src/compat/colors.ts new file mode 100644 index 000000000000..9489134e240f --- /dev/null +++ b/packages/tailwindcss/src/compat/colors.ts @@ -0,0 +1,293 @@ +export default { + inherit: 'inherit', + current: 'currentColor', + transparent: 'transparent', + black: '#000', + white: '#fff', + slate: { + 50: '#f8fafc', + 100: '#f1f5f9', + 200: '#e2e8f0', + 300: '#cbd5e1', + 400: '#94a3b8', + 500: '#64748b', + 600: '#475569', + 700: '#334155', + 800: '#1e293b', + 900: '#0f172a', + 950: '#020617', + }, + gray: { + 50: '#f9fafb', + 100: '#f3f4f6', + 200: '#e5e7eb', + 300: '#d1d5db', + 400: '#9ca3af', + 500: '#6b7280', + 600: '#4b5563', + 700: '#374151', + 800: '#1f2937', + 900: '#111827', + 950: '#030712', + }, + zinc: { + 50: '#fafafa', + 100: '#f4f4f5', + 200: '#e4e4e7', + 300: '#d4d4d8', + 400: '#a1a1aa', + 500: '#71717a', + 600: '#52525b', + 700: '#3f3f46', + 800: '#27272a', + 900: '#18181b', + 950: '#09090b', + }, + neutral: { + 50: '#fafafa', + 100: '#f5f5f5', + 200: '#e5e5e5', + 300: '#d4d4d4', + 400: '#a3a3a3', + 500: '#737373', + 600: '#525252', + 700: '#404040', + 800: '#262626', + 900: '#171717', + 950: '#0a0a0a', + }, + stone: { + 50: '#fafaf9', + 100: '#f5f5f4', + 200: '#e7e5e4', + 300: '#d6d3d1', + 400: '#a8a29e', + 500: '#78716c', + 600: '#57534e', + 700: '#44403c', + 800: '#292524', + 900: '#1c1917', + 950: '#0c0a09', + }, + red: { + 50: '#fef2f2', + 100: '#fee2e2', + 200: '#fecaca', + 300: '#fca5a5', + 400: '#f87171', + 500: '#ef4444', + 600: '#dc2626', + 700: '#b91c1c', + 800: '#991b1b', + 900: '#7f1d1d', + 950: '#450a0a', + }, + orange: { + 50: '#fff7ed', + 100: '#ffedd5', + 200: '#fed7aa', + 300: '#fdba74', + 400: '#fb923c', + 500: '#f97316', + 600: '#ea580c', + 700: '#c2410c', + 800: '#9a3412', + 900: '#7c2d12', + 950: '#431407', + }, + amber: { + 50: '#fffbeb', + 100: '#fef3c7', + 200: '#fde68a', + 300: '#fcd34d', + 400: '#fbbf24', + 500: '#f59e0b', + 600: '#d97706', + 700: '#b45309', + 800: '#92400e', + 900: '#78350f', + 950: '#451a03', + }, + yellow: { + 50: '#fefce8', + 100: '#fef9c3', + 200: '#fef08a', + 300: '#fde047', + 400: '#facc15', + 500: '#eab308', + 600: '#ca8a04', + 700: '#a16207', + 800: '#854d0e', + 900: '#713f12', + 950: '#422006', + }, + lime: { + 50: '#f7fee7', + 100: '#ecfccb', + 200: '#d9f99d', + 300: '#bef264', + 400: '#a3e635', + 500: '#84cc16', + 600: '#65a30d', + 700: '#4d7c0f', + 800: '#3f6212', + 900: '#365314', + 950: '#1a2e05', + }, + green: { + 50: '#f0fdf4', + 100: '#dcfce7', + 200: '#bbf7d0', + 300: '#86efac', + 400: '#4ade80', + 500: '#22c55e', + 600: '#16a34a', + 700: '#15803d', + 800: '#166534', + 900: '#14532d', + 950: '#052e16', + }, + emerald: { + 50: '#ecfdf5', + 100: '#d1fae5', + 200: '#a7f3d0', + 300: '#6ee7b7', + 400: '#34d399', + 500: '#10b981', + 600: '#059669', + 700: '#047857', + 800: '#065f46', + 900: '#064e3b', + 950: '#022c22', + }, + teal: { + 50: '#f0fdfa', + 100: '#ccfbf1', + 200: '#99f6e4', + 300: '#5eead4', + 400: '#2dd4bf', + 500: '#14b8a6', + 600: '#0d9488', + 700: '#0f766e', + 800: '#115e59', + 900: '#134e4a', + 950: '#042f2e', + }, + cyan: { + 50: '#ecfeff', + 100: '#cffafe', + 200: '#a5f3fc', + 300: '#67e8f9', + 400: '#22d3ee', + 500: '#06b6d4', + 600: '#0891b2', + 700: '#0e7490', + 800: '#155e75', + 900: '#164e63', + 950: '#083344', + }, + sky: { + 50: '#f0f9ff', + 100: '#e0f2fe', + 200: '#bae6fd', + 300: '#7dd3fc', + 400: '#38bdf8', + 500: '#0ea5e9', + 600: '#0284c7', + 700: '#0369a1', + 800: '#075985', + 900: '#0c4a6e', + 950: '#082f49', + }, + blue: { + 50: '#eff6ff', + 100: '#dbeafe', + 200: '#bfdbfe', + 300: '#93c5fd', + 400: '#60a5fa', + 500: '#3b82f6', + 600: '#2563eb', + 700: '#1d4ed8', + 800: '#1e40af', + 900: '#1e3a8a', + 950: '#172554', + }, + indigo: { + 50: '#eef2ff', + 100: '#e0e7ff', + 200: '#c7d2fe', + 300: '#a5b4fc', + 400: '#818cf8', + 500: '#6366f1', + 600: '#4f46e5', + 700: '#4338ca', + 800: '#3730a3', + 900: '#312e81', + 950: '#1e1b4b', + }, + violet: { + 50: '#f5f3ff', + 100: '#ede9fe', + 200: '#ddd6fe', + 300: '#c4b5fd', + 400: '#a78bfa', + 500: '#8b5cf6', + 600: '#7c3aed', + 700: '#6d28d9', + 800: '#5b21b6', + 900: '#4c1d95', + 950: '#2e1065', + }, + purple: { + 50: '#faf5ff', + 100: '#f3e8ff', + 200: '#e9d5ff', + 300: '#d8b4fe', + 400: '#c084fc', + 500: '#a855f7', + 600: '#9333ea', + 700: '#7e22ce', + 800: '#6b21a8', + 900: '#581c87', + 950: '#3b0764', + }, + fuchsia: { + 50: '#fdf4ff', + 100: '#fae8ff', + 200: '#f5d0fe', + 300: '#f0abfc', + 400: '#e879f9', + 500: '#d946ef', + 600: '#c026d3', + 700: '#a21caf', + 800: '#86198f', + 900: '#701a75', + 950: '#4a044e', + }, + pink: { + 50: '#fdf2f8', + 100: '#fce7f3', + 200: '#fbcfe8', + 300: '#f9a8d4', + 400: '#f472b6', + 500: '#ec4899', + 600: '#db2777', + 700: '#be185d', + 800: '#9d174d', + 900: '#831843', + 950: '#500724', + }, + rose: { + 50: '#fff1f2', + 100: '#ffe4e6', + 200: '#fecdd3', + 300: '#fda4af', + 400: '#fb7185', + 500: '#f43f5e', + 600: '#e11d48', + 700: '#be123c', + 800: '#9f1239', + 900: '#881337', + 950: '#4c0519', + }, +} diff --git a/packages/tailwindcss/tsup.config.ts b/packages/tailwindcss/tsup.config.ts index 77ccda19b28e..9df1779d052c 100644 --- a/packages/tailwindcss/tsup.config.ts +++ b/packages/tailwindcss/tsup.config.ts @@ -16,6 +16,7 @@ export default defineConfig([ dts: true, entry: { plugin: 'src/plugin.ts', + colors: 'src/compat/colors.ts', }, }, { @@ -24,6 +25,7 @@ export default defineConfig([ dts: true, entry: { plugin: 'src/plugin.cts', + colors: 'src/compat/colors.cts', }, }, ]) From f71147a91bd041fad6e28132db1a01168a0f158d Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 19 Aug 2024 13:24:52 -0400 Subject: [PATCH 03/25] Add support for arrays in `objectToAst` --- packages/tailwindcss/src/plugin-api.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/tailwindcss/src/plugin-api.ts b/packages/tailwindcss/src/plugin-api.ts index 9140a777260c..bab0294c004d 100644 --- a/packages/tailwindcss/src/plugin-api.ts +++ b/packages/tailwindcss/src/plugin-api.ts @@ -218,12 +218,16 @@ function buildPluginApi( } } -export type CssInJs = { [key: string]: string | CssInJs } +export type CssInJs = { [key: string]: string | CssInJs | CssInJs[] } -function objectToAst(obj: CssInJs): AstNode[] { +function objectToAst(rules: CssInJs | CssInJs[]): AstNode[] { let ast: AstNode[] = [] - for (let [name, value] of Object.entries(obj)) { + rules = Array.isArray(rules) ? rules : [rules] + + let entries = rules.flatMap((rule) => Object.entries(rule)) + + for (let [name, value] of entries) { if (typeof value !== 'object') { if (!name.startsWith('--') && value === '@slot') { ast.push(rule(name, [rule('@slot', [])])) From 920c22970fca485ff6849f6166cf99f1635d932f Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 19 Aug 2024 14:03:42 -0400 Subject: [PATCH 04/25] Add support for arrays passed to `addUtilities` --- packages/tailwindcss/src/plugin-api.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/tailwindcss/src/plugin-api.ts b/packages/tailwindcss/src/plugin-api.ts index bab0294c004d..b255c973f8e3 100644 --- a/packages/tailwindcss/src/plugin-api.ts +++ b/packages/tailwindcss/src/plugin-api.ts @@ -22,7 +22,8 @@ export type Plugin = PluginFn | PluginWithConfig | PluginWithOptions export type PluginAPI = { addBase(base: CssInJs): void addVariant(name: string, variant: string | string[] | CssInJs): void - addUtilities(utilities: Record, options?: {}): void + + addUtilities(utilities: Record | Record[], options?: {}): void matchUtilities( utilities: Record CssInJs>, options?: Partial<{ @@ -71,7 +72,11 @@ function buildPluginApi( }, addUtilities(utilities) { - for (let [name, css] of Object.entries(utilities)) { + utilities = Array.isArray(utilities) ? utilities : [utilities] + + let entries = utilities.flatMap((u) => Object.entries(u)) + + for (let [name, css] of entries) { if (name.startsWith('@keyframes ')) { ast.push(rule(name, objectToAst(css))) continue From b6a9f2af93c34cb8ce94178cbbf470bee9ecb8b4 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 19 Aug 2024 14:04:09 -0400 Subject: [PATCH 05/25] Add support for `addComponents` and `matchComponents` --- packages/tailwindcss/src/plugin-api.ts | 30 +++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/tailwindcss/src/plugin-api.ts b/packages/tailwindcss/src/plugin-api.ts index b255c973f8e3..7623b7a98e81 100644 --- a/packages/tailwindcss/src/plugin-api.ts +++ b/packages/tailwindcss/src/plugin-api.ts @@ -35,6 +35,20 @@ export type PluginAPI = { modifiers: 'any' | Record }>, ): void + + addComponents(utilities: Record | Record[], options?: {}): void + matchComponents( + utilities: Record CssInJs>, + options?: Partial<{ + type: string | string[] + supportsNegativeValues: boolean + values: Record & { + __BARE_VALUE__?: (value: NamedUtilityValue) => string | undefined + } + modifiers: 'any' | Record + }>, + ): void + theme(path: string, defaultValue?: any): any } @@ -45,7 +59,7 @@ function buildPluginApi( ast: AstNode[], resolvedConfig: { theme?: Record }, ): PluginAPI { - return { + let api: PluginAPI = { addBase(css) { ast.push(rule('@layer base', objectToAst(css))) }, @@ -215,12 +229,26 @@ function buildPluginApi( } }, + addComponents(components, options) { + this.addUtilities(components) + }, + + matchComponents(components, options) { + this.matchUtilities(components) + }, + theme: createThemeFn( designSystem, () => resolvedConfig.theme ?? {}, (value) => value, ), } + + // Bind these functions so they can use `this` + api.addComponents = api.addComponents.bind(api) + api.matchComponents = api.matchComponents.bind(api) + + return api } export type CssInJs = { [key: string]: string | CssInJs | CssInJs[] } From 5590f4200e33a8e02005deeba845c166413c6e66 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 19 Aug 2024 14:02:49 -0400 Subject: [PATCH 06/25] Add support for `prefix` fn --- packages/tailwindcss/src/plugin-api.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/tailwindcss/src/plugin-api.ts b/packages/tailwindcss/src/plugin-api.ts index 7623b7a98e81..5d221fad6d27 100644 --- a/packages/tailwindcss/src/plugin-api.ts +++ b/packages/tailwindcss/src/plugin-api.ts @@ -50,6 +50,7 @@ export type PluginAPI = { ): void theme(path: string, defaultValue?: any): any + prefix(className: string): string } const IS_VALID_UTILITY_NAME = /^[a-z][a-zA-Z0-9/%._-]*$/ @@ -242,6 +243,10 @@ function buildPluginApi( () => resolvedConfig.theme ?? {}, (value) => value, ), + + prefix(className) { + return className + }, } // Bind these functions so they can use `this` From c6d5a1977d8bd7fe06a0b1b283f50e0b2cf55054 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 19 Aug 2024 14:07:01 -0400 Subject: [PATCH 07/25] Add typography plugin to playground --- playgrounds/vite/package.json | 1 + playgrounds/vite/src/app.css | 1 + playgrounds/vite/src/app.tsx | 34 ++++++++++++++++++++++++++---- playgrounds/vite/src/typography.js | 1 + 4 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 playgrounds/vite/src/typography.js diff --git a/playgrounds/vite/package.json b/playgrounds/vite/package.json index 561ef06d33f5..90df668aad42 100644 --- a/playgrounds/vite/package.json +++ b/playgrounds/vite/package.json @@ -16,6 +16,7 @@ "tailwindcss": "workspace:^" }, "devDependencies": { + "@tailwindcss/typography": "^0.5.14", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "bun": "^1.1.22", diff --git a/playgrounds/vite/src/app.css b/playgrounds/vite/src/app.css index adbd8e19f9b0..eddb712e6eba 100644 --- a/playgrounds/vite/src/app.css +++ b/playgrounds/vite/src/app.css @@ -1,2 +1,3 @@ @import 'tailwindcss'; @plugin "./plugin.js"; +@plugin "./typography.js"; diff --git a/playgrounds/vite/src/app.tsx b/playgrounds/vite/src/app.tsx index 285bd41439de..3579390c038a 100644 --- a/playgrounds/vite/src/app.tsx +++ b/playgrounds/vite/src/app.tsx @@ -1,11 +1,37 @@ +// prettier-ignore import { Foo } from './foo' export function App() { return ( -
-

Hello World

- - +
+
+

Hello World

+ + +
+
+

Headline

+

+ Until now, trying to style an article, document, or blog post with Tailwind has been a + tedious task that required a keen eye for typography and a lot of complex custom CSS. +

+

+ By default, Tailwind removes all of the default browser styling from paragraphs, headings, + lists and more. This ends up being really useful for building application UIs because you + spend less time undoing user-agent styles, but when you really are just trying to + style some content that came from a rich-text editor in a CMS or a markdown file, it can + be surprising and unintuitive. +

+

+ We get lots of complaints about it actually, with people regularly asking us things like: +

+
+

+ Why is Tailwind removing the default styles on my h1 elements? How do I + disable this? What do you mean I lose all the other base styles too? +

+
+
) } diff --git a/playgrounds/vite/src/typography.js b/playgrounds/vite/src/typography.js new file mode 100644 index 000000000000..711af27f791f --- /dev/null +++ b/playgrounds/vite/src/typography.js @@ -0,0 +1 @@ +module.exports = require('@tailwindcss/typography') From 4643108bafc36bb7138679e136076647db494567 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 19 Aug 2024 14:13:02 -0400 Subject: [PATCH 08/25] wip --- pnpm-lock.yaml | 54 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7f7cee20f35c..ed43b0d678da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -302,6 +302,9 @@ importers: specifier: workspace:^ version: link:../../packages/tailwindcss devDependencies: + '@tailwindcss/typography': + specifier: ^0.5.14 + version: 0.5.14(tailwindcss@packages+tailwindcss) '@types/react': specifier: ^18.3.3 version: 18.3.3 @@ -1034,6 +1037,11 @@ packages: '@swc/helpers@0.5.2': resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + '@tailwindcss/typography@0.5.14': + resolution: {integrity: sha512-ZvOCjUbsJBjL9CxQBn+VEnFpouzuKhxh2dH8xMIWHILL+HfOYtlAkWcyoon8LlzE53d2Yo6YO6pahKKNW3q1YQ==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1249,7 +1257,6 @@ packages: bun@1.1.22: resolution: {integrity: sha512-G2HCPhzhjDc2jEDkZsO9vwPlpHrTm7a8UVwx9oNS5bZqo5OcSK5GPuWYDWjj7+37bRk5OVLfeIvUMtSrbKeIjQ==} - cpu: [arm64, x64] os: [darwin, linux, win32] hasBin: true @@ -1332,6 +1339,11 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -2077,6 +2089,12 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.castarray@4.4.0: + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -2353,6 +2371,10 @@ packages: yaml: optional: true + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -2800,6 +2822,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vite-node@2.0.5: resolution: {integrity: sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3441,6 +3466,14 @@ snapshots: dependencies: tslib: 2.6.3 + '@tailwindcss/typography@0.5.14(tailwindcss@packages+tailwindcss)': + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: link:packages/tailwindcss + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.25.3 @@ -3811,6 +3844,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + cssesc@3.0.0: {} + csstype@3.1.3: {} damerau-levenshtein@1.0.8: {} @@ -4078,7 +4113,7 @@ snapshots: eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -4102,7 +4137,7 @@ snapshots: enhanced-resolve: 5.17.1 eslint: 8.57.0 eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.6 is-core-module: 2.15.0 @@ -4124,7 +4159,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -4744,6 +4779,10 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.castarray@4.4.0: {} + + lodash.isplainobject@4.0.6: {} + lodash.merge@4.6.2: {} lodash.sortby@4.7.0: {} @@ -4988,6 +5027,11 @@ snapshots: optionalDependencies: postcss: 8.4.41 + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss-value-parser@4.2.0: {} postcss@8.4.31: @@ -5463,6 +5507,8 @@ snapshots: dependencies: punycode: 2.3.1 + util-deprecate@1.0.2: {} + vite-node@2.0.5(@types/node@20.14.13)(lightningcss@1.26.0): dependencies: cac: 6.7.14 From 6d5cbaeabe1ec58a451b5b90897911ebac8db17d Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 19 Aug 2024 15:50:03 -0400 Subject: [PATCH 09/25] Add `tailwindcss/defaultTheme` export --- packages/tailwindcss/package.json | 8 +++++++ .../tailwindcss/src/compat/default-theme.cts | 1 + .../tailwindcss/src/compat/default-theme.ts | 23 +++++++++++++++++++ packages/tailwindcss/tsup.config.ts | 3 ++- 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 packages/tailwindcss/src/compat/default-theme.cts create mode 100644 packages/tailwindcss/src/compat/default-theme.ts diff --git a/packages/tailwindcss/package.json b/packages/tailwindcss/package.json index 0655dcb21e52..4a6c4b26e76f 100644 --- a/packages/tailwindcss/package.json +++ b/packages/tailwindcss/package.json @@ -27,6 +27,10 @@ "require": "./src/compat/colors.cts", "import": "./src/compat/colors.ts" }, + "./defaultTheme": { + "require": "./src/compat/default-theme.cts", + "import": "./src/compat/default-theme.ts" + }, "./plugin": { "require": "./src/plugin.cts", "import": "./src/plugin.ts" @@ -55,6 +59,10 @@ "require": "./dist/plugin.js", "import": "./dist/plugin.mjs" }, + "./defaultTheme": { + "require": "./dist/default-theme.js", + "import": "./dist/default-theme.mjs" + }, "./colors": { "require": "./dist/colors.js", "import": "./dist/colors.mjs" diff --git a/packages/tailwindcss/src/compat/default-theme.cts b/packages/tailwindcss/src/compat/default-theme.cts new file mode 100644 index 000000000000..bc1750d7efec --- /dev/null +++ b/packages/tailwindcss/src/compat/default-theme.cts @@ -0,0 +1 @@ +module.exports = require('./default-theme.ts').default diff --git a/packages/tailwindcss/src/compat/default-theme.ts b/packages/tailwindcss/src/compat/default-theme.ts new file mode 100644 index 000000000000..2a90d269fd27 --- /dev/null +++ b/packages/tailwindcss/src/compat/default-theme.ts @@ -0,0 +1,23 @@ +import { Theme } from '../theme' +import { createCompatConfig } from './config/create-compat-config' + +let theme = new Theme() + +export default { + ...createCompatConfig(theme).theme, + fontSize: { + base: ['1rem', { lineHeight: '1.5rem' }], + }, + spacing: { + 2: '0.5rem', + 3: '0.75rem', + 4: '1rem', + 10: '2.5rem', + }, + borderWidth: { + DEFAULT: '1px', + }, + borderRadius: { + none: '0', + }, +} diff --git a/packages/tailwindcss/tsup.config.ts b/packages/tailwindcss/tsup.config.ts index 9df1779d052c..25803da45289 100644 --- a/packages/tailwindcss/tsup.config.ts +++ b/packages/tailwindcss/tsup.config.ts @@ -3,7 +3,6 @@ import { defineConfig } from 'tsup' export default defineConfig([ { format: ['esm', 'cjs'], - clean: true, minify: true, dts: true, entry: { @@ -17,6 +16,7 @@ export default defineConfig([ entry: { plugin: 'src/plugin.ts', colors: 'src/compat/colors.ts', + 'default-theme': 'src/compat/default-theme.ts', }, }, { @@ -26,6 +26,7 @@ export default defineConfig([ entry: { plugin: 'src/plugin.cts', colors: 'src/compat/colors.cts', + 'default-theme': 'src/compat/default-theme.cts', }, }, ]) From e00b5595e29fba11199e45aeb6e7043673abe6a8 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 21 Aug 2024 08:38:49 -0400 Subject: [PATCH 10/25] wip --- playgrounds/vite/src/app.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playgrounds/vite/src/app.tsx b/playgrounds/vite/src/app.tsx index 3579390c038a..0818c9658e53 100644 --- a/playgrounds/vite/src/app.tsx +++ b/playgrounds/vite/src/app.tsx @@ -9,9 +9,9 @@ export function App() {
-
+

Headline

-

+

Until now, trying to style an article, document, or blog post with Tailwind has been a tedious task that required a keen eye for typography and a lot of complex custom CSS.

From 9be14a088be88be65602f7e36026b1c78c710e94 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 21 Aug 2024 09:36:26 -0400 Subject: [PATCH 11/25] Support multiple selector keys in `addUtilities` --- packages/tailwindcss/src/plugin-api.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/tailwindcss/src/plugin-api.ts b/packages/tailwindcss/src/plugin-api.ts index 5d221fad6d27..1997e3c4c472 100644 --- a/packages/tailwindcss/src/plugin-api.ts +++ b/packages/tailwindcss/src/plugin-api.ts @@ -8,6 +8,7 @@ import type { DesignSystem } from './design-system' import { createThemeFn } from './theme-fn' import { withAlpha, withNegative } from './utilities' import { inferDataType } from './utils/infer-data-type' +import { segment } from './utils/segment' export type Config = UserConfig export type PluginFn = (api: PluginAPI) => void @@ -91,6 +92,11 @@ function buildPluginApi( let entries = utilities.flatMap((u) => Object.entries(u)) + // Split multi-selector utilities into individual utilities + entries = entries.flatMap(([name, css]) => + segment(name, ',').map((selector) => [selector.trim(), css] as [string, CssInJs]), + ) + for (let [name, css] of entries) { if (name.startsWith('@keyframes ')) { ast.push(rule(name, objectToAst(css))) From 88d3497a1abcee986ea7adf710a6caee7f314586 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 21 Aug 2024 09:40:33 -0400 Subject: [PATCH 12/25] Handle pseudo-classes/-elements in addUtilities --- packages/tailwindcss/src/plugin-api.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/tailwindcss/src/plugin-api.ts b/packages/tailwindcss/src/plugin-api.ts index 1997e3c4c472..381d4fcb0e93 100644 --- a/packages/tailwindcss/src/plugin-api.ts +++ b/packages/tailwindcss/src/plugin-api.ts @@ -97,7 +97,25 @@ function buildPluginApi( segment(name, ',').map((selector) => [selector.trim(), css] as [string, CssInJs]), ) + // Merge entries for the same class + let utils: Record = {} + for (let [name, css] of entries) { + let [className, ...parts] = segment(name, ':') + + // Modify classes using pseudo-classes or pseudo-elements to use nested rules + if (parts.length > 0) { + let pseudos = parts.map((p) => `:${p.trim()}`).join('') + css = { + [`&${pseudos}`]: css, + } + } + + utils[className] ??= [] + utils[className].push(css) + } + + for (let [name, css] of Object.entries(utils)) { if (name.startsWith('@keyframes ')) { ast.push(rule(name, objectToAst(css))) continue From 5a4a52234524c48c7c229da5f821ee870577d6ee Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 21 Aug 2024 09:40:55 -0400 Subject: [PATCH 13/25] Add forms plugin to vite playground --- playgrounds/vite/package.json | 1 + playgrounds/vite/src/forms.js | 1 + 2 files changed, 2 insertions(+) create mode 100644 playgrounds/vite/src/forms.js diff --git a/playgrounds/vite/package.json b/playgrounds/vite/package.json index 90df668aad42..92e37608b307 100644 --- a/playgrounds/vite/package.json +++ b/playgrounds/vite/package.json @@ -16,6 +16,7 @@ "tailwindcss": "workspace:^" }, "devDependencies": { + "@tailwindcss/forms": "^0.5.7", "@tailwindcss/typography": "^0.5.14", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", diff --git a/playgrounds/vite/src/forms.js b/playgrounds/vite/src/forms.js new file mode 100644 index 000000000000..f288ddfa0763 --- /dev/null +++ b/playgrounds/vite/src/forms.js @@ -0,0 +1 @@ +module.exports = require('@tailwindcss/forms') From 6b6ec0c7f3d6fab323769c93a33b14d355ca28da Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 21 Aug 2024 09:41:01 -0400 Subject: [PATCH 14/25] wip --- pnpm-lock.yaml | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed43b0d678da..10215e2f3cb6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -302,6 +302,9 @@ importers: specifier: workspace:^ version: link:../../packages/tailwindcss devDependencies: + '@tailwindcss/forms': + specifier: ^0.5.7 + version: 0.5.7(tailwindcss@packages+tailwindcss) '@tailwindcss/typography': specifier: ^0.5.14 version: 0.5.14(tailwindcss@packages+tailwindcss) @@ -1037,6 +1040,11 @@ packages: '@swc/helpers@0.5.2': resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + '@tailwindcss/forms@0.5.7': + resolution: {integrity: sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==} + peerDependencies: + tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1' + '@tailwindcss/typography@0.5.14': resolution: {integrity: sha512-ZvOCjUbsJBjL9CxQBn+VEnFpouzuKhxh2dH8xMIWHILL+HfOYtlAkWcyoon8LlzE53d2Yo6YO6pahKKNW3q1YQ==} peerDependencies: @@ -1257,6 +1265,7 @@ packages: bun@1.1.22: resolution: {integrity: sha512-G2HCPhzhjDc2jEDkZsO9vwPlpHrTm7a8UVwx9oNS5bZqo5OcSK5GPuWYDWjj7+37bRk5OVLfeIvUMtSrbKeIjQ==} + cpu: [arm64, x64] os: [darwin, linux, win32] hasBin: true @@ -2139,6 +2148,10 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + mini-svg-data-uri@1.4.4: + resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} + hasBin: true + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3466,6 +3479,11 @@ snapshots: dependencies: tslib: 2.6.3 + '@tailwindcss/forms@0.5.7(tailwindcss@packages+tailwindcss)': + dependencies: + mini-svg-data-uri: 1.4.4 + tailwindcss: link:packages/tailwindcss + '@tailwindcss/typography@0.5.14(tailwindcss@packages+tailwindcss)': dependencies: lodash.castarray: 4.4.0 @@ -4113,7 +4131,7 @@ snapshots: eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -4137,7 +4155,7 @@ snapshots: enhanced-resolve: 5.17.1 eslint: 8.57.0 eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.6 is-core-module: 2.15.0 @@ -4159,7 +4177,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -4820,6 +4838,8 @@ snapshots: mimic-fn@4.0.0: {} + mini-svg-data-uri@1.4.4: {} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 From e157310065f4ac7c766bc4315475d167e35a40ae Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 21 Aug 2024 09:41:06 -0400 Subject: [PATCH 15/25] wip --- playgrounds/vite/src/app.css | 1 + 1 file changed, 1 insertion(+) diff --git a/playgrounds/vite/src/app.css b/playgrounds/vite/src/app.css index eddb712e6eba..e46d27063fc7 100644 --- a/playgrounds/vite/src/app.css +++ b/playgrounds/vite/src/app.css @@ -1,3 +1,4 @@ @import 'tailwindcss'; @plugin "./plugin.js"; @plugin "./typography.js"; +@plugin "./forms.js"; From 30481df7eeb091f2ad31ac83c83f39eb9bb6389b Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 21 Aug 2024 09:41:16 -0400 Subject: [PATCH 16/25] wip --- playgrounds/vite/src/app.tsx | 220 ++++++++++++++++++++++++++++++++++- 1 file changed, 219 insertions(+), 1 deletion(-) diff --git a/playgrounds/vite/src/app.tsx b/playgrounds/vite/src/app.tsx index 0818c9658e53..74db3b1528ab 100644 --- a/playgrounds/vite/src/app.tsx +++ b/playgrounds/vite/src/app.tsx @@ -1,4 +1,3 @@ -// prettier-ignore import { Foo } from './foo' export function App() { @@ -32,6 +31,225 @@ export function App() {

+
+
+
+

Reset styles

+

+ These are form elements this plugin styles by default. +

+
+
+ + + + + + + + + + + +
+
+ + + + + + + +
+ Checkboxes +
+
+ +
+
+ +
+
+ +
+
+
+
+ Radio Buttons +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+

Untouched

+

+ These are form elements we don't handle (yet?), but we use this to make sure we + haven't accidentally styled them by mistake. +

+
+
+ + + + +
+
+
+
+
) } From 2e856727dc6d05f7aeae6f2dbee52ec414cd7580 Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Thu, 22 Aug 2024 12:31:05 +0200 Subject: [PATCH 17/25] Add test for addComponents() --- packages/tailwindcss/src/plugin-api.test.ts | 989 +++++++++++++++++++- packages/tailwindcss/src/plugin-api.ts | 6 +- packages/tailwindcss/src/utilities.test.ts | 846 ----------------- 3 files changed, 992 insertions(+), 849 deletions(-) diff --git a/packages/tailwindcss/src/plugin-api.test.ts b/packages/tailwindcss/src/plugin-api.test.ts index c02521df0cee..2e892997d9bf 100644 --- a/packages/tailwindcss/src/plugin-api.test.ts +++ b/packages/tailwindcss/src/plugin-api.test.ts @@ -1,6 +1,8 @@ -import { describe, test, vi } from 'vitest' +import { describe, expect, test, vi } from 'vitest' import { compile } from '.' import plugin from './plugin' +import type { CssInJs, PluginAPI } from './plugin-api' +import { optimizeCss } from './test-utils/run' const css = String.raw @@ -813,3 +815,988 @@ describe('theme', async () => { expect(fn).toHaveBeenCalledWith({}) // Present in the resolved config }) }) + +describe('addUtilities()', () => { + test('custom static utility', async () => { + let compiled = await compile( + css` + @plugin "my-plugin"; + @layer utilities { + @tailwind utilities; + } + + @theme reference { + --breakpoint-lg: 1024px; + } + `, + { + async loadPlugin() { + return ({ addUtilities }: PluginAPI) => { + addUtilities({ + '.text-trim': { + 'text-box-trim': 'both', + 'text-box-edge': 'cap alphabetic', + }, + }) + } + }, + }, + ) + + expect(optimizeCss(compiled.build(['text-trim', 'lg:text-trim'])).trim()) + .toMatchInlineSnapshot(` + "@layer utilities { + .text-trim { + text-box-trim: both; + text-box-edge: cap alphabetic; + } + + @media (width >= 1024px) { + .lg\\:text-trim { + text-box-trim: both; + text-box-edge: cap alphabetic; + } + } + }" + `) + }) + + test('define multiple utilities with array syntax', async () => { + let compiled = await compile( + css` + @plugin "my-plugin"; + @tailwind utilities; + `, + { + async loadPlugin() { + return ({ addUtilities }: PluginAPI) => { + addUtilities([ + { + '.text-trim': { + 'text-box-trim': 'both', + 'text-box-edge': 'cap alphabetic', + }, + }, + { + '.text-trim-2': { + 'text-box-trim': 'both', + 'text-box-edge': 'cap alphabetic', + }, + }, + ]) + } + }, + }, + ) + + expect(optimizeCss(compiled.build(['text-trim', 'text-trim-2'])).trim()).toMatchInlineSnapshot(` + ".text-trim, .text-trim-2 { + text-box-trim: both; + text-box-edge: cap alphabetic; + }" + `) + }) + + test('camel case properties are converted to kebab-case', async () => { + let compiled = await compile( + css` + @plugin "my-plugin"; + @layer utilities { + @tailwind utilities; + } + `, + { + async loadPlugin() { + return ({ addUtilities }: PluginAPI) => { + addUtilities({ + '.text-trim': { + WebkitAppearance: 'none', + textBoxTrim: 'both', + textBoxEdge: 'cap alphabetic', + }, + }) + } + }, + }, + ) + + expect(optimizeCss(compiled.build(['text-trim'])).trim()).toMatchInlineSnapshot(` + "@layer utilities { + .text-trim { + -webkit-appearance: none; + text-box-trim: both; + text-box-edge: cap alphabetic; + } + }" + `) + }) + + test('custom static utilities support `@apply`', async () => { + let compiled = await compile( + css` + @plugin "my-plugin"; + @layer utilities { + @tailwind utilities; + } + + @theme reference { + --breakpoint-lg: 1024px; + } + `, + { + async loadPlugin() { + return ({ addUtilities }: PluginAPI) => { + addUtilities({ + '.foo': { + '@apply flex dark:underline': {}, + }, + }) + } + }, + }, + ) + + expect(optimizeCss(compiled.build(['foo', 'lg:foo'])).trim()).toMatchInlineSnapshot(` + "@layer utilities { + .foo { + display: flex; + } + + @media (prefers-color-scheme: dark) { + .foo { + text-decoration-line: underline; + } + } + + @media (width >= 1024px) { + .lg\\:foo { + display: flex; + } + + @media (prefers-color-scheme: dark) { + .lg\\:foo { + text-decoration-line: underline; + } + } + } + }" + `) + }) + + test('throws on custom static utilities with an invalid name', async () => { + await expect(() => { + return compile( + css` + @plugin "my-plugin"; + @layer utilities { + @tailwind utilities; + } + + @theme reference { + --breakpoint-lg: 1024px; + } + `, + { + async loadPlugin() { + return ({ addUtilities }: PluginAPI) => { + addUtilities({ + '.text-trim > *': { + 'text-box-trim': 'both', + 'text-box-edge': 'cap alphabetic', + }, + }) + } + }, + }, + ) + }).rejects.toThrowError(/invalid utility selector/) + }) +}) + +describe('matchUtilities()', () => { + test('custom functional utility', async () => { + async function run(candidates: string[]) { + let compiled = await compile( + css` + @plugin "my-plugin"; + + @tailwind utilities; + + @theme reference { + --breakpoint-lg: 1024px; + } + `, + { + async loadPlugin() { + return ({ matchUtilities }: PluginAPI) => { + matchUtilities( + { + 'border-block': (value) => ({ 'border-block-width': value }), + }, + { + values: { + DEFAULT: '1px', + '2': '2px', + }, + }, + ) + } + }, + }, + ) + + return compiled.build(candidates) + } + + expect( + optimizeCss( + await run([ + 'border-block', + 'border-block-2', + 'border-block-[35px]', + 'border-block-[var(--foo)]', + 'lg:border-block-2', + ]), + ).trim(), + ).toMatchInlineSnapshot(` + ".border-block { + border-block-width: 1px; + } + + .border-block-2 { + border-block-width: 2px; + } + + .border-block-\\[35px\\] { + border-block-width: 35px; + } + + .border-block-\\[var\\(--foo\\)\\] { + border-block-width: var(--foo); + } + + @media (width >= 1024px) { + .lg\\:border-block-2 { + border-block-width: 2px; + } + }" + `) + + expect( + optimizeCss( + await run([ + '-border-block', + '-border-block-2', + 'lg:-border-block-2', + 'border-block-unknown', + 'border-block/1', + ]), + ).trim(), + ).toEqual('') + }) + + test('custom functional utilities can return an array of rules', async () => { + let compiled = await compile( + css` + @plugin "my-plugin"; + @tailwind utilities; + `, + { + async loadPlugin() { + return ({ matchUtilities }: PluginAPI) => { + matchUtilities( + { + 'all-but-order-bottom-left-radius': (value) => + [ + { 'border-top-left-radius': value }, + { 'border-top-right-radius': value }, + { 'border-bottom-right-radius': value }, + ] as CssInJs[], + }, + { + values: { + DEFAULT: '1px', + }, + }, + ) + } + }, + }, + ) + + expect(optimizeCss(compiled.build(['all-but-order-bottom-left-radius'])).trim()) + .toMatchInlineSnapshot(` + ".all-but-order-bottom-left-radius { + border-top-left-radius: 1px; + border-top-right-radius: 1px; + border-bottom-right-radius: 1px; + }" + `) + }) + + test('custom functional utility with any modifier', async () => { + async function run(candidates: string[]) { + let compiled = await compile( + css` + @plugin "my-plugin"; + + @tailwind utilities; + + @theme reference { + --breakpoint-lg: 1024px; + } + `, + { + async loadPlugin() { + return ({ matchUtilities }: PluginAPI) => { + matchUtilities( + { + 'border-block': (value, { modifier }) => ({ + '--my-modifier': modifier ?? 'none', + 'border-block-width': value, + }), + }, + { + values: { + DEFAULT: '1px', + '2': '2px', + }, + + modifiers: 'any', + }, + ) + } + }, + }, + ) + + return compiled.build(candidates) + } + + expect( + optimizeCss( + await run(['border-block', 'border-block-2', 'border-block/foo', 'border-block-2/foo']), + ).trim(), + ).toMatchInlineSnapshot(` + ".border-block { + --my-modifier: none; + border-block-width: 1px; + } + + .border-block-2 { + --my-modifier: none; + border-block-width: 2px; + } + + .border-block-2\\/foo { + --my-modifier: foo; + border-block-width: 2px; + } + + .border-block\\/foo { + --my-modifier: foo; + border-block-width: 1px; + }" + `) + }) + + test('custom functional utility with known modifier', async () => { + async function run(candidates: string[]) { + let compiled = await compile( + css` + @plugin "my-plugin"; + + @tailwind utilities; + + @theme reference { + --breakpoint-lg: 1024px; + } + `, + { + async loadPlugin() { + return ({ matchUtilities }: PluginAPI) => { + matchUtilities( + { + 'border-block': (value, { modifier }) => ({ + '--my-modifier': modifier ?? 'none', + 'border-block-width': value, + }), + }, + { + values: { + DEFAULT: '1px', + '2': '2px', + }, + + modifiers: { + foo: 'foo', + }, + }, + ) + } + }, + }, + ) + + return compiled.build(candidates) + } + + expect( + optimizeCss( + await run(['border-block', 'border-block-2', 'border-block/foo', 'border-block-2/foo']), + ).trim(), + ).toMatchInlineSnapshot(` + ".border-block { + --my-modifier: none; + border-block-width: 1px; + } + + .border-block-2 { + --my-modifier: none; + border-block-width: 2px; + } + + .border-block-2\\/foo { + --my-modifier: foo; + border-block-width: 2px; + } + + .border-block\\/foo { + --my-modifier: foo; + border-block-width: 1px; + }" + `) + + expect( + optimizeCss(await run(['border-block/unknown', 'border-block-2/unknown'])).trim(), + ).toEqual('') + }) + + // We're not married to this behavior — if there's a good reason to do this differently in the + // future don't be afraid to change what should happen in this scenario. + describe('plugins that handle a specific arbitrary value type prevent falling through to other plugins if the result is invalid for that plugin', () => { + test('implicit color modifier', async () => { + async function run(candidates: string[]) { + let compiled = await compile( + css` + @tailwind utilities; + @plugin "my-plugin"; + `, + { + async loadPlugin() { + return ({ matchUtilities }: PluginAPI) => { + matchUtilities( + { + scrollbar: (value) => ({ 'scrollbar-color': value }), + }, + { type: ['color', 'any'] }, + ) + + matchUtilities( + { + scrollbar: (value) => ({ 'scrollbar-width': value }), + }, + { type: ['length'] }, + ) + } + }, + }, + ) + + return compiled.build(candidates) + } + + expect( + optimizeCss( + await run(['scrollbar-[2px]', 'scrollbar-[#08c]', 'scrollbar-[#08c]/50']), + ).trim(), + ).toMatchInlineSnapshot(` + ".scrollbar-\\[\\#08c\\] { + scrollbar-color: #08c; + } + + .scrollbar-\\[\\#08c\\]\\/50 { + scrollbar-color: #0088cc80; + } + + .scrollbar-\\[2px\\] { + scrollbar-width: 2px; + }" + `) + expect(optimizeCss(await run(['scrollbar-[2px]/50'])).trim()).toEqual('') + }) + + test('no modifiers are supported by the plugins', async () => { + async function run(candidates: string[]) { + let compiled = await compile( + css` + @tailwind utilities; + @plugin "my-plugin"; + `, + { + async loadPlugin() { + return ({ matchUtilities }: PluginAPI) => { + matchUtilities( + { + scrollbar: (value) => ({ '--scrollbar-angle': value }), + }, + { type: ['angle', 'any'] }, + ) + + matchUtilities( + { + scrollbar: (value) => ({ '--scrollbar-width': value }), + }, + { type: ['length'] }, + ) + } + }, + }, + ) + + return compiled.build(candidates) + } + + expect(optimizeCss(await run(['scrollbar-[2px]/50'])).trim()).toEqual('') + }) + + test('invalid named modifier', async () => { + async function run(candidates: string[]) { + let compiled = await compile( + css` + @tailwind utilities; + @plugin "my-plugin"; + `, + { + async loadPlugin() { + return ({ matchUtilities }: PluginAPI) => { + matchUtilities( + { + scrollbar: (value) => ({ 'scrollbar-color': value }), + }, + { type: ['color', 'any'], modifiers: { foo: 'foo' } }, + ) + + matchUtilities( + { + scrollbar: (value) => ({ 'scrollbar-width': value }), + }, + { type: ['length'], modifiers: { bar: 'bar' } }, + ) + } + }, + }, + ) + + return compiled.build(candidates) + } + + expect(optimizeCss(await run(['scrollbar-[2px]/foo'])).trim()).toEqual('') + }) + }) + + test('custom functional utilities with different types', async () => { + async function run(candidates: string[]) { + let compiled = await compile( + css` + @plugin "my-plugin"; + + @tailwind utilities; + + @theme reference { + --breakpoint-lg: 1024px; + } + `, + { + async loadPlugin() { + return ({ matchUtilities }: PluginAPI) => { + matchUtilities( + { + scrollbar: (value) => ({ 'scrollbar-color': value }), + }, + { + type: ['color', 'any'], + values: { + black: 'black', + }, + }, + ) + + matchUtilities( + { + scrollbar: (value) => ({ 'scrollbar-width': value }), + }, + { + type: ['length'], + values: { + 2: '2px', + }, + }, + ) + } + }, + }, + ) + + return compiled.build(candidates) + } + + expect( + optimizeCss( + await run([ + 'scrollbar-black', + 'scrollbar-black/50', + 'scrollbar-2', + 'scrollbar-[#fff]', + 'scrollbar-[#fff]/50', + 'scrollbar-[2px]', + 'scrollbar-[var(--my-color)]', + 'scrollbar-[var(--my-color)]/50', + 'scrollbar-[color:var(--my-color)]', + 'scrollbar-[color:var(--my-color)]/50', + 'scrollbar-[length:var(--my-width)]', + ]), + ).trim(), + ).toMatchInlineSnapshot(` + ".scrollbar-2 { + scrollbar-width: 2px; + } + + .scrollbar-\\[\\#fff\\] { + scrollbar-color: #fff; + } + + .scrollbar-\\[\\#fff\\]\\/50 { + scrollbar-color: #ffffff80; + } + + .scrollbar-\\[2px\\] { + scrollbar-width: 2px; + } + + .scrollbar-\\[color\\:var\\(--my-color\\)\\] { + scrollbar-color: var(--my-color); + } + + .scrollbar-\\[color\\:var\\(--my-color\\)\\]\\/50 { + scrollbar-color: color-mix(in srgb, var(--my-color) 50%, transparent); + } + + .scrollbar-\\[length\\:var\\(--my-width\\)\\] { + scrollbar-width: var(--my-width); + } + + .scrollbar-\\[var\\(--my-color\\)\\] { + scrollbar-color: var(--my-color); + } + + .scrollbar-\\[var\\(--my-color\\)\\]\\/50 { + scrollbar-color: color-mix(in srgb, var(--my-color) 50%, transparent); + } + + .scrollbar-black { + scrollbar-color: black; + } + + .scrollbar-black\\/50 { + scrollbar-color: #00000080; + }" + `) + + expect( + optimizeCss( + await run([ + 'scrollbar-2/50', + 'scrollbar-[2px]/50', + 'scrollbar-[length:var(--my-width)]/50', + ]), + ).trim(), + ).toEqual('') + }) + + test('functional utilities with `type: color` automatically support opacity', async () => { + async function run(candidates: string[]) { + let compiled = await compile( + css` + @plugin "my-plugin"; + + @tailwind utilities; + + @theme reference { + --breakpoint-lg: 1024px; + } + `, + { + async loadPlugin() { + return ({ matchUtilities }: PluginAPI) => { + matchUtilities( + { + scrollbar: (value) => ({ 'scrollbar-color': value }), + }, + { + type: ['color', 'any'], + values: { + black: 'black', + }, + }, + ) + } + }, + }, + ) + + return compiled.build(candidates) + } + + expect( + optimizeCss( + await run([ + 'scrollbar-current', + 'scrollbar-current/45', + 'scrollbar-black', + 'scrollbar-black/33', + 'scrollbar-black/[50%]', + 'scrollbar-[var(--my-color)]/[25%]', + ]), + ).trim(), + ).toMatchInlineSnapshot(` + ".scrollbar-\\[var\\(--my-color\\)\\]\\/\\[25\\%\\] { + scrollbar-color: color-mix(in srgb, var(--my-color) 25%, transparent); + } + + .scrollbar-black { + scrollbar-color: black; + } + + .scrollbar-black\\/33 { + scrollbar-color: #00000054; + } + + .scrollbar-black\\/\\[50\\%\\] { + scrollbar-color: #00000080; + } + + .scrollbar-current { + scrollbar-color: currentColor; + } + + .scrollbar-current\\/45 { + scrollbar-color: color-mix(in srgb, currentColor 45%, transparent); + }" + `) + }) + + test('functional utilities with explicit modifiers', async () => { + async function run(candidates: string[]) { + let compiled = await compile( + css` + @plugin "my-plugin"; + + @tailwind utilities; + + @theme reference { + --breakpoint-lg: 1024px; + --opacity-my-opacity: 0.5; + } + `, + { + async loadPlugin() { + return ({ matchUtilities }: PluginAPI) => { + matchUtilities( + { + scrollbar: (value, { modifier }) => ({ + '--modifier': modifier ?? 'none', + 'scrollbar-width': value, + }), + }, + { + type: ['any'], + values: {}, + modifiers: { + foo: 'foo', + }, + }, + ) + } + }, + }, + ) + + return compiled.build(candidates) + } + + expect( + optimizeCss( + await run(['scrollbar-[12px]', 'scrollbar-[12px]/foo', 'scrollbar-[12px]/bar']), + ).trim(), + ).toMatchInlineSnapshot(` + ".scrollbar-\\[12px\\] { + --modifier: none; + scrollbar-width: 12px; + } + + .scrollbar-\\[12px\\]\\/foo { + --modifier: foo; + scrollbar-width: 12px; + }" + `) + }) + + test('functional utilities support `@apply`', async () => { + let compiled = await compile( + css` + @plugin "my-plugin"; + @layer utilities { + @tailwind utilities; + } + + @theme reference { + --breakpoint-lg: 1024px; + } + `, + { + async loadPlugin() { + return ({ matchUtilities }: PluginAPI) => { + matchUtilities( + { + foo: (value) => ({ + '--foo': value, + [`@apply flex`]: {}, + }), + }, + { + values: { + bar: 'bar', + }, + }, + ) + } + }, + }, + ) + + expect( + optimizeCss(compiled.build(['foo-bar', 'lg:foo-bar', 'foo-[12px]', 'lg:foo-[12px]'])).trim(), + ).toMatchInlineSnapshot(` + "@layer utilities { + .foo-\\[12px\\] { + --foo: 12px; + display: flex; + } + + .foo-bar { + --foo: bar; + display: flex; + } + + @media (width >= 1024px) { + .lg\\:foo-\\[12px\\] { + --foo: 12px; + display: flex; + } + } + + @media (width >= 1024px) { + .lg\\:foo-bar { + --foo: bar; + display: flex; + } + } + }" + `) + }) + + test('throws on custom utilities with an invalid name', async () => { + await expect(() => { + return compile( + css` + @plugin "my-plugin"; + @layer utilities { + @tailwind utilities; + } + + @theme reference { + --breakpoint-lg: 1024px; + } + `, + { + async loadPlugin() { + return ({ matchUtilities }: PluginAPI) => { + matchUtilities({ + '.text-trim > *': () => ({ + 'text-box-trim': 'both', + 'text-box-edge': 'cap alphabetic', + }), + }) + } + }, + }, + ) + }).rejects.toThrowError(/invalid utility name/) + }) +}) + +describe('addComponents()', () => { + test('is an alias for addUtilities', async () => { + let compiled = await compile( + css` + @plugin "my-plugin"; + @tailwind utilities; + `, + { + async loadPlugin() { + return ({ addComponents }: PluginAPI) => { + addComponents({ + '.btn': { + padding: '.5rem 1rem', + borderRadius: '.25rem', + fontWeight: '600', + }, + '.btn-blue': { + backgroundColor: '#3490dc', + color: '#fff', + '&:hover': { + backgroundColor: '#2779bd', + }, + }, + '.btn-red': { + backgroundColor: '#e3342f', + color: '#fff', + '&:hover': { + backgroundColor: '#cc1f1a', + }, + }, + }) + } + }, + }, + ) + + expect(optimizeCss(compiled.build(['btn', 'btn-blue', 'btn-red'])).trim()) + .toMatchInlineSnapshot(` + ".btn { + border-radius: .25rem; + padding: .5rem 1rem; + font-weight: 600; + } + + .btn-blue { + color: #fff; + background-color: #3490dc; + } + + .btn-blue:hover { + background-color: #2779bd; + } + + .btn-red { + color: #fff; + background-color: #e3342f; + } + + .btn-red:hover { + background-color: #cc1f1a; + }" + `) + }) +}) diff --git a/packages/tailwindcss/src/plugin-api.ts b/packages/tailwindcss/src/plugin-api.ts index 381d4fcb0e93..becaf4a40d26 100644 --- a/packages/tailwindcss/src/plugin-api.ts +++ b/packages/tailwindcss/src/plugin-api.ts @@ -26,7 +26,10 @@ export type PluginAPI = { addUtilities(utilities: Record | Record[], options?: {}): void matchUtilities( - utilities: Record CssInJs>, + utilities: Record< + string, + (value: string, extra: { modifier: string | null }) => CssInJs | CssInJs[] + >, options?: Partial<{ type: string | string[] supportsNegativeValues: boolean @@ -129,7 +132,6 @@ function buildPluginApi( designSystem.utilities.static(name.slice(1), (candidate) => { if (candidate.negative) return - let ast = objectToAst(css) substituteAtApply(ast, designSystem) return ast diff --git a/packages/tailwindcss/src/utilities.test.ts b/packages/tailwindcss/src/utilities.test.ts index a01e8473f048..0d04f7caa4d2 100644 --- a/packages/tailwindcss/src/utilities.test.ts +++ b/packages/tailwindcss/src/utilities.test.ts @@ -1,6 +1,5 @@ import { describe, expect, test } from 'vitest' import { compile } from '.' -import type { PluginAPI } from './plugin-api' import { compileCss, optimizeCss, run } from './test-utils/run' const css = String.raw @@ -15396,848 +15395,3 @@ describe('custom utilities', () => { ) }) }) - -describe('legacy: addUtilities', () => { - test('custom static utility', async () => { - let compiled = await compile( - css` - @plugin "my-plugin"; - @layer utilities { - @tailwind utilities; - } - - @theme reference { - --breakpoint-lg: 1024px; - } - `, - { - async loadPlugin() { - return ({ addUtilities }: PluginAPI) => { - addUtilities({ - '.text-trim': { - 'text-box-trim': 'both', - 'text-box-edge': 'cap alphabetic', - }, - }) - } - }, - }, - ) - - expect(optimizeCss(compiled.build(['text-trim', 'lg:text-trim'])).trim()) - .toMatchInlineSnapshot(` - "@layer utilities { - .text-trim { - text-box-trim: both; - text-box-edge: cap alphabetic; - } - - @media (width >= 1024px) { - .lg\\:text-trim { - text-box-trim: both; - text-box-edge: cap alphabetic; - } - } - }" - `) - }) - - test('camel case properties are converted to kebab-case', async () => { - let compiled = await compile( - css` - @plugin "my-plugin"; - @layer utilities { - @tailwind utilities; - } - `, - { - async loadPlugin() { - return ({ addUtilities }: PluginAPI) => { - addUtilities({ - '.text-trim': { - WebkitAppearance: 'none', - textBoxTrim: 'both', - textBoxEdge: 'cap alphabetic', - }, - }) - } - }, - }, - ) - - expect(optimizeCss(compiled.build(['text-trim'])).trim()).toMatchInlineSnapshot(` - "@layer utilities { - .text-trim { - -webkit-appearance: none; - text-box-trim: both; - text-box-edge: cap alphabetic; - } - }" - `) - }) - - test('custom static utilities support `@apply`', async () => { - let compiled = await compile( - css` - @plugin "my-plugin"; - @layer utilities { - @tailwind utilities; - } - - @theme reference { - --breakpoint-lg: 1024px; - } - `, - { - async loadPlugin() { - return ({ addUtilities }: PluginAPI) => { - addUtilities({ - '.foo': { - '@apply flex dark:underline': {}, - }, - }) - } - }, - }, - ) - - expect(optimizeCss(compiled.build(['foo', 'lg:foo'])).trim()).toMatchInlineSnapshot(` - "@layer utilities { - .foo { - display: flex; - } - - @media (prefers-color-scheme: dark) { - .foo { - text-decoration-line: underline; - } - } - - @media (width >= 1024px) { - .lg\\:foo { - display: flex; - } - - @media (prefers-color-scheme: dark) { - .lg\\:foo { - text-decoration-line: underline; - } - } - } - }" - `) - }) - - test('throws on custom static utilities with an invalid name', async () => { - await expect(() => { - return compile( - css` - @plugin "my-plugin"; - @layer utilities { - @tailwind utilities; - } - - @theme reference { - --breakpoint-lg: 1024px; - } - `, - { - async loadPlugin() { - return ({ addUtilities }: PluginAPI) => { - addUtilities({ - '.text-trim > *': { - 'text-box-trim': 'both', - 'text-box-edge': 'cap alphabetic', - }, - }) - } - }, - }, - ) - }).rejects.toThrowError(/invalid utility selector/) - }) -}) - -describe('legacy: matchUtilities', () => { - test('custom functional utility', async () => { - async function run(candidates: string[]) { - let compiled = await compile( - css` - @plugin "my-plugin"; - - @tailwind utilities; - - @theme reference { - --breakpoint-lg: 1024px; - } - `, - { - async loadPlugin() { - return ({ matchUtilities }: PluginAPI) => { - matchUtilities( - { - 'border-block': (value) => ({ 'border-block-width': value }), - }, - { - values: { - DEFAULT: '1px', - '2': '2px', - }, - }, - ) - } - }, - }, - ) - - return compiled.build(candidates) - } - - expect( - optimizeCss( - await run([ - 'border-block', - 'border-block-2', - 'border-block-[35px]', - 'border-block-[var(--foo)]', - 'lg:border-block-2', - ]), - ).trim(), - ).toMatchInlineSnapshot(` - ".border-block { - border-block-width: 1px; - } - - .border-block-2 { - border-block-width: 2px; - } - - .border-block-\\[35px\\] { - border-block-width: 35px; - } - - .border-block-\\[var\\(--foo\\)\\] { - border-block-width: var(--foo); - } - - @media (width >= 1024px) { - .lg\\:border-block-2 { - border-block-width: 2px; - } - }" - `) - - expect( - optimizeCss( - await run([ - '-border-block', - '-border-block-2', - 'lg:-border-block-2', - 'border-block-unknown', - 'border-block/1', - ]), - ).trim(), - ).toEqual('') - }) - - test('custom functional utility with any modifier', async () => { - async function run(candidates: string[]) { - let compiled = await compile( - css` - @plugin "my-plugin"; - - @tailwind utilities; - - @theme reference { - --breakpoint-lg: 1024px; - } - `, - { - async loadPlugin() { - return ({ matchUtilities }: PluginAPI) => { - matchUtilities( - { - 'border-block': (value, { modifier }) => ({ - '--my-modifier': modifier ?? 'none', - 'border-block-width': value, - }), - }, - { - values: { - DEFAULT: '1px', - '2': '2px', - }, - - modifiers: 'any', - }, - ) - } - }, - }, - ) - - return compiled.build(candidates) - } - - expect( - optimizeCss( - await run(['border-block', 'border-block-2', 'border-block/foo', 'border-block-2/foo']), - ).trim(), - ).toMatchInlineSnapshot(` - ".border-block { - --my-modifier: none; - border-block-width: 1px; - } - - .border-block-2 { - --my-modifier: none; - border-block-width: 2px; - } - - .border-block-2\\/foo { - --my-modifier: foo; - border-block-width: 2px; - } - - .border-block\\/foo { - --my-modifier: foo; - border-block-width: 1px; - }" - `) - }) - - test('custom functional utility with known modifier', async () => { - async function run(candidates: string[]) { - let compiled = await compile( - css` - @plugin "my-plugin"; - - @tailwind utilities; - - @theme reference { - --breakpoint-lg: 1024px; - } - `, - { - async loadPlugin() { - return ({ matchUtilities }: PluginAPI) => { - matchUtilities( - { - 'border-block': (value, { modifier }) => ({ - '--my-modifier': modifier ?? 'none', - 'border-block-width': value, - }), - }, - { - values: { - DEFAULT: '1px', - '2': '2px', - }, - - modifiers: { - foo: 'foo', - }, - }, - ) - } - }, - }, - ) - - return compiled.build(candidates) - } - - expect( - optimizeCss( - await run(['border-block', 'border-block-2', 'border-block/foo', 'border-block-2/foo']), - ).trim(), - ).toMatchInlineSnapshot(` - ".border-block { - --my-modifier: none; - border-block-width: 1px; - } - - .border-block-2 { - --my-modifier: none; - border-block-width: 2px; - } - - .border-block-2\\/foo { - --my-modifier: foo; - border-block-width: 2px; - } - - .border-block\\/foo { - --my-modifier: foo; - border-block-width: 1px; - }" - `) - - expect( - optimizeCss(await run(['border-block/unknown', 'border-block-2/unknown'])).trim(), - ).toEqual('') - }) - - // We're not married to this behavior — if there's a good reason to do this differently in the - // future don't be afraid to change what should happen in this scenario. - describe('plugins that handle a specific arbitrary value type prevent falling through to other plugins if the result is invalid for that plugin', () => { - test('implicit color modifier', async () => { - async function run(candidates: string[]) { - let compiled = await compile( - css` - @tailwind utilities; - @plugin "my-plugin"; - `, - { - async loadPlugin() { - return ({ matchUtilities }: PluginAPI) => { - matchUtilities( - { - scrollbar: (value) => ({ 'scrollbar-color': value }), - }, - { type: ['color', 'any'] }, - ) - - matchUtilities( - { - scrollbar: (value) => ({ 'scrollbar-width': value }), - }, - { type: ['length'] }, - ) - } - }, - }, - ) - - return compiled.build(candidates) - } - - expect( - optimizeCss( - await run(['scrollbar-[2px]', 'scrollbar-[#08c]', 'scrollbar-[#08c]/50']), - ).trim(), - ).toMatchInlineSnapshot(` - ".scrollbar-\\[\\#08c\\] { - scrollbar-color: #08c; - } - - .scrollbar-\\[\\#08c\\]\\/50 { - scrollbar-color: #0088cc80; - } - - .scrollbar-\\[2px\\] { - scrollbar-width: 2px; - }" - `) - expect(optimizeCss(await run(['scrollbar-[2px]/50'])).trim()).toEqual('') - }) - - test('no modifiers are supported by the plugins', async () => { - async function run(candidates: string[]) { - let compiled = await compile( - css` - @tailwind utilities; - @plugin "my-plugin"; - `, - { - async loadPlugin() { - return ({ matchUtilities }: PluginAPI) => { - matchUtilities( - { - scrollbar: (value) => ({ '--scrollbar-angle': value }), - }, - { type: ['angle', 'any'] }, - ) - - matchUtilities( - { - scrollbar: (value) => ({ '--scrollbar-width': value }), - }, - { type: ['length'] }, - ) - } - }, - }, - ) - - return compiled.build(candidates) - } - - expect(optimizeCss(await run(['scrollbar-[2px]/50'])).trim()).toEqual('') - }) - - test('invalid named modifier', async () => { - async function run(candidates: string[]) { - let compiled = await compile( - css` - @tailwind utilities; - @plugin "my-plugin"; - `, - { - async loadPlugin() { - return ({ matchUtilities }: PluginAPI) => { - matchUtilities( - { - scrollbar: (value) => ({ 'scrollbar-color': value }), - }, - { type: ['color', 'any'], modifiers: { foo: 'foo' } }, - ) - - matchUtilities( - { - scrollbar: (value) => ({ 'scrollbar-width': value }), - }, - { type: ['length'], modifiers: { bar: 'bar' } }, - ) - } - }, - }, - ) - - return compiled.build(candidates) - } - - expect(optimizeCss(await run(['scrollbar-[2px]/foo'])).trim()).toEqual('') - }) - }) - - test('custom functional utilities with different types', async () => { - async function run(candidates: string[]) { - let compiled = await compile( - css` - @plugin "my-plugin"; - - @tailwind utilities; - - @theme reference { - --breakpoint-lg: 1024px; - } - `, - { - async loadPlugin() { - return ({ matchUtilities }: PluginAPI) => { - matchUtilities( - { - scrollbar: (value) => ({ 'scrollbar-color': value }), - }, - { - type: ['color', 'any'], - values: { - black: 'black', - }, - }, - ) - - matchUtilities( - { - scrollbar: (value) => ({ 'scrollbar-width': value }), - }, - { - type: ['length'], - values: { - 2: '2px', - }, - }, - ) - } - }, - }, - ) - - return compiled.build(candidates) - } - - expect( - optimizeCss( - await run([ - 'scrollbar-black', - 'scrollbar-black/50', - 'scrollbar-2', - 'scrollbar-[#fff]', - 'scrollbar-[#fff]/50', - 'scrollbar-[2px]', - 'scrollbar-[var(--my-color)]', - 'scrollbar-[var(--my-color)]/50', - 'scrollbar-[color:var(--my-color)]', - 'scrollbar-[color:var(--my-color)]/50', - 'scrollbar-[length:var(--my-width)]', - ]), - ).trim(), - ).toMatchInlineSnapshot(` - ".scrollbar-2 { - scrollbar-width: 2px; - } - - .scrollbar-\\[\\#fff\\] { - scrollbar-color: #fff; - } - - .scrollbar-\\[\\#fff\\]\\/50 { - scrollbar-color: #ffffff80; - } - - .scrollbar-\\[2px\\] { - scrollbar-width: 2px; - } - - .scrollbar-\\[color\\:var\\(--my-color\\)\\] { - scrollbar-color: var(--my-color); - } - - .scrollbar-\\[color\\:var\\(--my-color\\)\\]\\/50 { - scrollbar-color: color-mix(in srgb, var(--my-color) 50%, transparent); - } - - .scrollbar-\\[length\\:var\\(--my-width\\)\\] { - scrollbar-width: var(--my-width); - } - - .scrollbar-\\[var\\(--my-color\\)\\] { - scrollbar-color: var(--my-color); - } - - .scrollbar-\\[var\\(--my-color\\)\\]\\/50 { - scrollbar-color: color-mix(in srgb, var(--my-color) 50%, transparent); - } - - .scrollbar-black { - scrollbar-color: black; - } - - .scrollbar-black\\/50 { - scrollbar-color: #00000080; - }" - `) - - expect( - optimizeCss( - await run([ - 'scrollbar-2/50', - 'scrollbar-[2px]/50', - 'scrollbar-[length:var(--my-width)]/50', - ]), - ).trim(), - ).toEqual('') - }) - - test('functional utilities with `type: color` automatically support opacity', async () => { - async function run(candidates: string[]) { - let compiled = await compile( - css` - @plugin "my-plugin"; - - @tailwind utilities; - - @theme reference { - --breakpoint-lg: 1024px; - } - `, - { - async loadPlugin() { - return ({ matchUtilities }: PluginAPI) => { - matchUtilities( - { - scrollbar: (value) => ({ 'scrollbar-color': value }), - }, - { - type: ['color', 'any'], - values: { - black: 'black', - }, - }, - ) - } - }, - }, - ) - - return compiled.build(candidates) - } - - expect( - optimizeCss( - await run([ - 'scrollbar-current', - 'scrollbar-current/45', - 'scrollbar-black', - 'scrollbar-black/33', - 'scrollbar-black/[50%]', - 'scrollbar-[var(--my-color)]/[25%]', - ]), - ).trim(), - ).toMatchInlineSnapshot(` - ".scrollbar-\\[var\\(--my-color\\)\\]\\/\\[25\\%\\] { - scrollbar-color: color-mix(in srgb, var(--my-color) 25%, transparent); - } - - .scrollbar-black { - scrollbar-color: black; - } - - .scrollbar-black\\/33 { - scrollbar-color: #00000054; - } - - .scrollbar-black\\/\\[50\\%\\] { - scrollbar-color: #00000080; - } - - .scrollbar-current { - scrollbar-color: currentColor; - } - - .scrollbar-current\\/45 { - scrollbar-color: color-mix(in srgb, currentColor 45%, transparent); - }" - `) - }) - - test('functional utilities with explicit modifiers', async () => { - async function run(candidates: string[]) { - let compiled = await compile( - css` - @plugin "my-plugin"; - - @tailwind utilities; - - @theme reference { - --breakpoint-lg: 1024px; - --opacity-my-opacity: 0.5; - } - `, - { - async loadPlugin() { - return ({ matchUtilities }: PluginAPI) => { - matchUtilities( - { - scrollbar: (value, { modifier }) => ({ - '--modifier': modifier ?? 'none', - 'scrollbar-width': value, - }), - }, - { - type: ['any'], - values: {}, - modifiers: { - foo: 'foo', - }, - }, - ) - } - }, - }, - ) - - return compiled.build(candidates) - } - - expect( - optimizeCss( - await run(['scrollbar-[12px]', 'scrollbar-[12px]/foo', 'scrollbar-[12px]/bar']), - ).trim(), - ).toMatchInlineSnapshot(` - ".scrollbar-\\[12px\\] { - --modifier: none; - scrollbar-width: 12px; - } - - .scrollbar-\\[12px\\]\\/foo { - --modifier: foo; - scrollbar-width: 12px; - }" - `) - }) - - test('functional utilities support `@apply`', async () => { - let compiled = await compile( - css` - @plugin "my-plugin"; - @layer utilities { - @tailwind utilities; - } - - @theme reference { - --breakpoint-lg: 1024px; - } - `, - { - async loadPlugin() { - return ({ matchUtilities }: PluginAPI) => { - matchUtilities( - { - foo: (value) => ({ - '--foo': value, - [`@apply flex`]: {}, - }), - }, - { - values: { - bar: 'bar', - }, - }, - ) - } - }, - }, - ) - - expect( - optimizeCss(compiled.build(['foo-bar', 'lg:foo-bar', 'foo-[12px]', 'lg:foo-[12px]'])).trim(), - ).toMatchInlineSnapshot(` - "@layer utilities { - .foo-\\[12px\\] { - --foo: 12px; - display: flex; - } - - .foo-bar { - --foo: bar; - display: flex; - } - - @media (width >= 1024px) { - .lg\\:foo-\\[12px\\] { - --foo: 12px; - display: flex; - } - } - - @media (width >= 1024px) { - .lg\\:foo-bar { - --foo: bar; - display: flex; - } - } - }" - `) - }) - - test('throws on custom utilities with an invalid name', async () => { - await expect(() => { - return compile( - css` - @plugin "my-plugin"; - @layer utilities { - @tailwind utilities; - } - - @theme reference { - --breakpoint-lg: 1024px; - } - `, - { - async loadPlugin() { - return ({ matchUtilities }: PluginAPI) => { - matchUtilities({ - '.text-trim > *': () => ({ - 'text-box-trim': 'both', - 'text-box-edge': 'cap alphabetic', - }), - }) - } - }, - }, - ) - }).rejects.toThrowError(/invalid utility name/) - }) -}) From e4a5e48721b4840056fb7b503ba8061d545157db Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Thu, 22 Aug 2024 12:33:07 +0200 Subject: [PATCH 18/25] Add test for prefix() --- packages/tailwindcss/src/plugin-api.test.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/tailwindcss/src/plugin-api.test.ts b/packages/tailwindcss/src/plugin-api.test.ts index 2e892997d9bf..78d1532025d3 100644 --- a/packages/tailwindcss/src/plugin-api.test.ts +++ b/packages/tailwindcss/src/plugin-api.test.ts @@ -1800,3 +1800,23 @@ describe('addComponents()', () => { `) }) }) + +describe('prefix()', () => { + test('is an identity function', async () => { + let fn = vi.fn() + await compile( + css` + @plugin "my-plugin"; + `, + { + async loadPlugin() { + return ({ prefix }: PluginAPI) => { + fn(prefix('btn')) + } + }, + }, + ) + + expect(fn).toHaveBeenCalledWith('btn') + }) +}) From c8126d6bf7bc872e74424262e986b0150db42e6c Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Thu, 22 Aug 2024 12:38:15 +0200 Subject: [PATCH 19/25] Add test for multiple selector names and pseudo classes/elements --- packages/tailwindcss/src/plugin-api.test.ts | 81 +++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/packages/tailwindcss/src/plugin-api.test.ts b/packages/tailwindcss/src/plugin-api.test.ts index 78d1532025d3..d36450bdc852 100644 --- a/packages/tailwindcss/src/plugin-api.test.ts +++ b/packages/tailwindcss/src/plugin-api.test.ts @@ -1011,6 +1011,87 @@ describe('addUtilities()', () => { ) }).rejects.toThrowError(/invalid utility selector/) }) + + test('supports multiple selector names', async () => { + let compiled = await compile( + css` + @plugin "my-plugin"; + @tailwind utilities; + + @theme reference { + --breakpoint-lg: 1024px; + } + `, + { + async loadPlugin() { + return ({ addUtilities }: PluginAPI) => { + addUtilities({ + '.form-input, .form-textarea': { + appearance: 'none', + 'background-color': '#fff', + }, + }) + } + }, + }, + ) + + expect(optimizeCss(compiled.build(['form-input', 'lg:form-textarea'])).trim()) + .toMatchInlineSnapshot(` + ".form-input { + appearance: none; + background-color: #fff; + } + + @media (width >= 1024px) { + .lg\\:form-textarea { + appearance: none; + background-color: #fff; + } + }" + `) + }) + + test('supports pseudo classes and pseudo elements', async () => { + let compiled = await compile( + css` + @plugin "my-plugin"; + @tailwind utilities; + + @theme reference { + --breakpoint-lg: 1024px; + } + `, + { + async loadPlugin() { + return ({ addUtilities }: PluginAPI) => { + addUtilities({ + '.form-input, .form-input::placeholder, .form-textarea:hover:focus': { + 'background-color': 'red', + }, + }) + } + }, + }, + ) + + expect(optimizeCss(compiled.build(['form-input', 'lg:form-textarea'])).trim()) + .toMatchInlineSnapshot(` + ".form-input { + background-color: red; + } + + .form-input::placeholder { + background-color: red; + } + + @media (width >= 1024px) { + .lg\\:form-textarea:hover:focus { + background-color: red; + } + }" + `) + }) }) describe('matchUtilities()', () => { From 779bb2be9e4877527311f0bc2e447fa209c118d9 Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Thu, 22 Aug 2024 13:02:53 +0200 Subject: [PATCH 20/25] Update default config static properties to match V3 --- .../src/compat/config/create-compat-config.ts | 10 +- .../tailwindcss/src/compat/default-theme.ts | 748 +++++++++++++++++- 2 files changed, 752 insertions(+), 6 deletions(-) diff --git a/packages/tailwindcss/src/compat/config/create-compat-config.ts b/packages/tailwindcss/src/compat/config/create-compat-config.ts index 540886f0d008..84801d0f986d 100644 --- a/packages/tailwindcss/src/compat/config/create-compat-config.ts +++ b/packages/tailwindcss/src/compat/config/create-compat-config.ts @@ -46,7 +46,10 @@ export function createCompatConfig(theme: Theme): UserConfig { return { theme: { colors: ({ theme }) => theme('color', {}), - accentColor: ({ theme }) => theme('colors'), + accentColor: ({ theme }) => ({ + ...theme('colors'), + auto: 'auto', + }), aspectRatio: bareValues((value) => { if (value.fraction === null) return let [lhs, rhs] = segment(value.fraction, '/') @@ -103,7 +106,10 @@ export function createCompatConfig(theme: Theme): UserConfig { ...theme('borderWidth'), ...barePixels, }), - fill: ({ theme }) => theme('colors'), + fill: ({ theme }) => ({ + none: 'none', + ...theme('colors'), + }), flexBasis: ({ theme }) => theme('spacing'), flexGrow: bareIntegers, flexShrink: bareIntegers, diff --git a/packages/tailwindcss/src/compat/default-theme.ts b/packages/tailwindcss/src/compat/default-theme.ts index 2a90d269fd27..c64b7136cc46 100644 --- a/packages/tailwindcss/src/compat/default-theme.ts +++ b/packages/tailwindcss/src/compat/default-theme.ts @@ -5,19 +5,759 @@ let theme = new Theme() export default { ...createCompatConfig(theme).theme, + animation: { + none: 'none', + spin: 'spin 1s linear infinite', + ping: 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite', + pulse: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite', + bounce: 'bounce 1s infinite', + }, + aria: { + busy: 'busy="true"', + checked: 'checked="true"', + disabled: 'disabled="true"', + expanded: 'expanded="true"', + hidden: 'hidden="true"', + pressed: 'pressed="true"', + readonly: 'readonly="true"', + required: 'required="true"', + selected: 'selected="true"', + }, + aspectRatio: { + auto: 'auto', + square: '1 / 1', + video: '16 / 9', + }, + backgroundImage: { + none: 'none', + 'gradient-to-t': 'linear-gradient(to top, var(--tw-gradient-stops))', + 'gradient-to-tr': 'linear-gradient(to top right, var(--tw-gradient-stops))', + 'gradient-to-r': 'linear-gradient(to right, var(--tw-gradient-stops))', + 'gradient-to-br': 'linear-gradient(to bottom right, var(--tw-gradient-stops))', + 'gradient-to-b': 'linear-gradient(to bottom, var(--tw-gradient-stops))', + 'gradient-to-bl': 'linear-gradient(to bottom left, var(--tw-gradient-stops))', + 'gradient-to-l': 'linear-gradient(to left, var(--tw-gradient-stops))', + 'gradient-to-tl': 'linear-gradient(to top left, var(--tw-gradient-stops))', + }, + backgroundPosition: { + bottom: 'bottom', + center: 'center', + left: 'left', + 'left-bottom': 'left bottom', + 'left-top': 'left top', + right: 'right', + 'right-bottom': 'right bottom', + 'right-top': 'right top', + top: 'top', + }, + backgroundSize: { + auto: 'auto', + cover: 'cover', + contain: 'contain', + }, + blur: { + 0: '0', + none: '', + sm: '4px', + DEFAULT: '8px', + md: '12px', + lg: '16px', + xl: '24px', + '2xl': '40px', + '3xl': '64px', + }, + borderRadius: { + none: '0px', + sm: '0.125rem', + DEFAULT: '0.25rem', + md: '0.375rem', + lg: '0.5rem', + xl: '0.75rem', + '2xl': '1rem', + '3xl': '1.5rem', + full: '9999px', + }, + borderWidth: { + DEFAULT: '1px', + 0: '0px', + 2: '2px', + 4: '4px', + 8: '8px', + }, + boxShadow: { + sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)', + DEFAULT: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)', + md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', + lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)', + xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)', + '2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)', + inner: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)', + none: 'none', + }, + brightness: { + 0: '0', + 50: '.5', + 75: '.75', + 90: '.9', + 95: '.95', + 100: '1', + 105: '1.05', + 110: '1.1', + 125: '1.25', + 150: '1.5', + 200: '2', + }, + columns: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + '3xs': '16rem', + '2xs': '18rem', + xs: '20rem', + sm: '24rem', + md: '28rem', + lg: '32rem', + xl: '36rem', + '2xl': '42rem', + '3xl': '48rem', + '4xl': '56rem', + '5xl': '64rem', + '6xl': '72rem', + '7xl': '80rem', + }, + container: {}, + content: { + none: 'none', + }, + contrast: { + 0: '0', + 50: '.5', + 75: '.75', + 100: '1', + 125: '1.25', + 150: '1.5', + 200: '2', + }, + cursor: { + auto: 'auto', + default: 'default', + pointer: 'pointer', + wait: 'wait', + text: 'text', + move: 'move', + help: 'help', + 'not-allowed': 'not-allowed', + none: 'none', + 'context-menu': 'context-menu', + progress: 'progress', + cell: 'cell', + crosshair: 'crosshair', + 'vertical-text': 'vertical-text', + alias: 'alias', + copy: 'copy', + 'no-drop': 'no-drop', + grab: 'grab', + grabbing: 'grabbing', + 'all-scroll': 'all-scroll', + 'col-resize': 'col-resize', + 'row-resize': 'row-resize', + 'n-resize': 'n-resize', + 'e-resize': 'e-resize', + 's-resize': 's-resize', + 'w-resize': 'w-resize', + 'ne-resize': 'ne-resize', + 'nw-resize': 'nw-resize', + 'se-resize': 'se-resize', + 'sw-resize': 'sw-resize', + 'ew-resize': 'ew-resize', + 'ns-resize': 'ns-resize', + 'nesw-resize': 'nesw-resize', + 'nwse-resize': 'nwse-resize', + 'zoom-in': 'zoom-in', + 'zoom-out': 'zoom-out', + }, + dropShadow: { + sm: '0 1px 1px rgb(0 0 0 / 0.05)', + DEFAULT: ['0 1px 2px rgb(0 0 0 / 0.1)', '0 1px 1px rgb(0 0 0 / 0.06)'], + md: ['0 4px 3px rgb(0 0 0 / 0.07)', '0 2px 2px rgb(0 0 0 / 0.06)'], + lg: ['0 10px 8px rgb(0 0 0 / 0.04)', '0 4px 3px rgb(0 0 0 / 0.1)'], + xl: ['0 20px 13px rgb(0 0 0 / 0.03)', '0 8px 5px rgb(0 0 0 / 0.08)'], + '2xl': '0 25px 25px rgb(0 0 0 / 0.15)', + none: '0 0 #0000', + }, + flex: { + 1: '1 1 0%', + auto: '1 1 auto', + initial: '0 1 auto', + none: 'none', + }, + flexGrow: { + 0: '0', + DEFAULT: '1', + }, + flexShrink: { + 0: '0', + DEFAULT: '1', + }, + fontFamily: { + sans: [ + 'ui-sans-serif', + 'system-ui', + 'sans-serif', + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"', + '"Noto Color Emoji"', + ], + serif: ['ui-serif', 'Georgia', 'Cambria', '"Times New Roman"', 'Times', 'serif'], + mono: [ + 'ui-monospace', + 'SFMono-Regular', + 'Menlo', + 'Monaco', + 'Consolas', + '"Liberation Mono"', + '"Courier New"', + 'monospace', + ], + }, fontSize: { + xs: ['0.75rem', { lineHeight: '1rem' }], + sm: ['0.875rem', { lineHeight: '1.25rem' }], base: ['1rem', { lineHeight: '1.5rem' }], + lg: ['1.125rem', { lineHeight: '1.75rem' }], + xl: ['1.25rem', { lineHeight: '1.75rem' }], + '2xl': ['1.5rem', { lineHeight: '2rem' }], + '3xl': ['1.875rem', { lineHeight: '2.25rem' }], + '4xl': ['2.25rem', { lineHeight: '2.5rem' }], + '5xl': ['3rem', { lineHeight: '1' }], + '6xl': ['3.75rem', { lineHeight: '1' }], + '7xl': ['4.5rem', { lineHeight: '1' }], + '8xl': ['6rem', { lineHeight: '1' }], + '9xl': ['8rem', { lineHeight: '1' }], + }, + fontWeight: { + thin: '100', + extralight: '200', + light: '300', + normal: '400', + medium: '500', + semibold: '600', + bold: '700', + extrabold: '800', + black: '900', + }, + gradientColorStopPositions: { + '0%': '0%', + '5%': '5%', + '10%': '10%', + '15%': '15%', + '20%': '20%', + '25%': '25%', + '30%': '30%', + '35%': '35%', + '40%': '40%', + '45%': '45%', + '50%': '50%', + '55%': '55%', + '60%': '60%', + '65%': '65%', + '70%': '70%', + '75%': '75%', + '80%': '80%', + '85%': '85%', + '90%': '90%', + '95%': '95%', + '100%': '100%', + }, + grayscale: { + 0: '0', + DEFAULT: '100%', + }, + gridAutoColumns: { + auto: 'auto', + min: 'min-content', + max: 'max-content', + fr: 'minmax(0, 1fr)', + }, + gridAutoRows: { + auto: 'auto', + min: 'min-content', + max: 'max-content', + fr: 'minmax(0, 1fr)', + }, + gridColumn: { + auto: 'auto', + 'span-1': 'span 1 / span 1', + 'span-2': 'span 2 / span 2', + 'span-3': 'span 3 / span 3', + 'span-4': 'span 4 / span 4', + 'span-5': 'span 5 / span 5', + 'span-6': 'span 6 / span 6', + 'span-7': 'span 7 / span 7', + 'span-8': 'span 8 / span 8', + 'span-9': 'span 9 / span 9', + 'span-10': 'span 10 / span 10', + 'span-11': 'span 11 / span 11', + 'span-12': 'span 12 / span 12', + 'span-full': '1 / -1', + }, + gridColumnEnd: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + 13: '13', + }, + gridColumnStart: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + 13: '13', + }, + gridRow: { + auto: 'auto', + 'span-1': 'span 1 / span 1', + 'span-2': 'span 2 / span 2', + 'span-3': 'span 3 / span 3', + 'span-4': 'span 4 / span 4', + 'span-5': 'span 5 / span 5', + 'span-6': 'span 6 / span 6', + 'span-7': 'span 7 / span 7', + 'span-8': 'span 8 / span 8', + 'span-9': 'span 9 / span 9', + 'span-10': 'span 10 / span 10', + 'span-11': 'span 11 / span 11', + 'span-12': 'span 12 / span 12', + 'span-full': '1 / -1', + }, + gridRowEnd: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + 13: '13', + }, + gridRowStart: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + 13: '13', + }, + gridTemplateColumns: { + none: 'none', + subgrid: 'subgrid', + 1: 'repeat(1, minmax(0, 1fr))', + 2: 'repeat(2, minmax(0, 1fr))', + 3: 'repeat(3, minmax(0, 1fr))', + 4: 'repeat(4, minmax(0, 1fr))', + 5: 'repeat(5, minmax(0, 1fr))', + 6: 'repeat(6, minmax(0, 1fr))', + 7: 'repeat(7, minmax(0, 1fr))', + 8: 'repeat(8, minmax(0, 1fr))', + 9: 'repeat(9, minmax(0, 1fr))', + 10: 'repeat(10, minmax(0, 1fr))', + 11: 'repeat(11, minmax(0, 1fr))', + 12: 'repeat(12, minmax(0, 1fr))', + }, + gridTemplateRows: { + none: 'none', + subgrid: 'subgrid', + 1: 'repeat(1, minmax(0, 1fr))', + 2: 'repeat(2, minmax(0, 1fr))', + 3: 'repeat(3, minmax(0, 1fr))', + 4: 'repeat(4, minmax(0, 1fr))', + 5: 'repeat(5, minmax(0, 1fr))', + 6: 'repeat(6, minmax(0, 1fr))', + 7: 'repeat(7, minmax(0, 1fr))', + 8: 'repeat(8, minmax(0, 1fr))', + 9: 'repeat(9, minmax(0, 1fr))', + 10: 'repeat(10, minmax(0, 1fr))', + 11: 'repeat(11, minmax(0, 1fr))', + 12: 'repeat(12, minmax(0, 1fr))', + }, + hueRotate: { + 0: '0deg', + 15: '15deg', + 30: '30deg', + 60: '60deg', + 90: '90deg', + 180: '180deg', + }, + invert: { + 0: '0', + DEFAULT: '100%', + }, + keyframes: { + spin: { + to: { + transform: 'rotate(360deg)', + }, + }, + ping: { + '75%, 100%': { + transform: 'scale(2)', + opacity: '0', + }, + }, + pulse: { + '50%': { + opacity: '.5', + }, + }, + bounce: { + '0%, 100%': { + transform: 'translateY(-25%)', + animationTimingFunction: 'cubic-bezier(0.8,0,1,1)', + }, + '50%': { + transform: 'none', + animationTimingFunction: 'cubic-bezier(0,0,0.2,1)', + }, + }, + }, + letterSpacing: { + tighter: '-0.05em', + tight: '-0.025em', + normal: '0em', + wide: '0.025em', + wider: '0.05em', + widest: '0.1em', + }, + lineHeight: { + none: '1', + tight: '1.25', + snug: '1.375', + normal: '1.5', + relaxed: '1.625', + loose: '2', + 3: '.75rem', + 4: '1rem', + 5: '1.25rem', + 6: '1.5rem', + 7: '1.75rem', + 8: '2rem', + 9: '2.25rem', + 10: '2.5rem', + }, + listStyleType: { + none: 'none', + disc: 'disc', + decimal: 'decimal', + }, + listStyleImage: { + none: 'none', + }, + lineClamp: { + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + }, + objectPosition: { + bottom: 'bottom', + center: 'center', + left: 'left', + 'left-bottom': 'left bottom', + 'left-top': 'left top', + right: 'right', + 'right-bottom': 'right bottom', + 'right-top': 'right top', + top: 'top', + }, + opacity: { + 0: '0', + 5: '0.05', + 10: '0.1', + 15: '0.15', + 20: '0.2', + 25: '0.25', + 30: '0.3', + 35: '0.35', + 40: '0.4', + 45: '0.45', + 50: '0.5', + 55: '0.55', + 60: '0.6', + 65: '0.65', + 70: '0.7', + 75: '0.75', + 80: '0.8', + 85: '0.85', + 90: '0.9', + 95: '0.95', + 100: '1', + }, + order: { + first: '-9999', + last: '9999', + none: '0', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + }, + outlineOffset: { + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px', + }, + outlineWidth: { + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px', + }, + ringOffsetWidth: { + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px', + }, + ringWidth: { + DEFAULT: '3px', + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px', + }, + rotate: { + 0: '0deg', + 1: '1deg', + 2: '2deg', + 3: '3deg', + 6: '6deg', + 12: '12deg', + 45: '45deg', + 90: '90deg', + 180: '180deg', + }, + saturate: { + 0: '0', + 50: '.5', + 100: '1', + 150: '1.5', + 200: '2', + }, + scale: { + 0: '0', + 50: '.5', + 75: '.75', + 90: '.9', + 95: '.95', + 100: '1', + 105: '1.05', + 110: '1.1', + 125: '1.25', + 150: '1.5', + }, + screens: { + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + '2xl': '1536px', + }, + sepia: { + 0: '0', + DEFAULT: '100%', + }, + skew: { + 0: '0deg', + 1: '1deg', + 2: '2deg', + 3: '3deg', + 6: '6deg', + 12: '12deg', }, spacing: { + px: '1px', + 0: '0px', + 0.5: '0.125rem', + 1: '0.25rem', + 1.5: '0.375rem', 2: '0.5rem', + 2.5: '0.625rem', 3: '0.75rem', + 3.5: '0.875rem', 4: '1rem', + 5: '1.25rem', + 6: '1.5rem', + 7: '1.75rem', + 8: '2rem', + 9: '2.25rem', 10: '2.5rem', + 11: '2.75rem', + 12: '3rem', + 14: '3.5rem', + 16: '4rem', + 20: '5rem', + 24: '6rem', + 28: '7rem', + 32: '8rem', + 36: '9rem', + 40: '10rem', + 44: '11rem', + 48: '12rem', + 52: '13rem', + 56: '14rem', + 60: '15rem', + 64: '16rem', + 72: '18rem', + 80: '20rem', + 96: '24rem', }, - borderWidth: { - DEFAULT: '1px', + strokeWidth: { + 0: '0', + 1: '1', + 2: '2', }, - borderRadius: { - none: '0', + supports: {}, + data: {}, + textDecorationThickness: { + auto: 'auto', + 'from-font': 'from-font', + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px', + }, + textUnderlineOffset: { + auto: 'auto', + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px', + }, + transformOrigin: { + center: 'center', + top: 'top', + 'top-right': 'top right', + right: 'right', + 'bottom-right': 'bottom right', + bottom: 'bottom', + 'bottom-left': 'bottom left', + left: 'left', + 'top-left': 'top left', + }, + transitionDelay: { + 0: '0s', + 75: '75ms', + 100: '100ms', + 150: '150ms', + 200: '200ms', + 300: '300ms', + 500: '500ms', + 700: '700ms', + 1000: '1000ms', + }, + transitionDuration: { + DEFAULT: '150ms', + 0: '0s', + 75: '75ms', + 100: '100ms', + 150: '150ms', + 200: '200ms', + 300: '300ms', + 500: '500ms', + 700: '700ms', + 1000: '1000ms', + }, + transitionProperty: { + none: 'none', + all: 'all', + DEFAULT: + 'color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter', + colors: 'color, background-color, border-color, text-decoration-color, fill, stroke', + opacity: 'opacity', + shadow: 'box-shadow', + transform: 'transform', + }, + transitionTimingFunction: { + DEFAULT: 'cubic-bezier(0.4, 0, 0.2, 1)', + linear: 'linear', + in: 'cubic-bezier(0.4, 0, 1, 1)', + out: 'cubic-bezier(0, 0, 0.2, 1)', + 'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)', + }, + willChange: { + auto: 'auto', + scroll: 'scroll-position', + contents: 'contents', + transform: 'transform', + }, + zIndex: { + auto: 'auto', + 0: '0', + 10: '10', + 20: '20', + 30: '30', + 40: '40', + 50: '50', }, } From 885f02e5527f2da00bb33645e99db77d5d1227b8 Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Thu, 22 Aug 2024 13:08:20 +0200 Subject: [PATCH 21/25] addUtilities can return an array of declaration objects --- packages/tailwindcss/src/plugin-api.test.ts | 27 +++++++++++++++++++++ packages/tailwindcss/src/plugin-api.ts | 8 ++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/tailwindcss/src/plugin-api.test.ts b/packages/tailwindcss/src/plugin-api.test.ts index d36450bdc852..c5d817a93493 100644 --- a/packages/tailwindcss/src/plugin-api.test.ts +++ b/packages/tailwindcss/src/plugin-api.test.ts @@ -861,6 +861,33 @@ describe('addUtilities()', () => { `) }) + test('return multiple rule objects from a custom utility', async () => { + let compiled = await compile( + css` + @plugin "my-plugin"; + @tailwind utilities; + `, + { + async loadPlugin() { + return ({ addUtilities }: PluginAPI) => { + addUtilities([ + { + '.text-trim': [{ 'text-box-trim': 'both' }, { 'text-box-edge': 'cap alphabetic' }], + }, + ]) + } + }, + }, + ) + + expect(optimizeCss(compiled.build(['text-trim'])).trim()).toMatchInlineSnapshot(` + ".text-trim { + text-box-trim: both; + text-box-edge: cap alphabetic; + }" + `) + }) + test('define multiple utilities with array syntax', async () => { let compiled = await compile( css` diff --git a/packages/tailwindcss/src/plugin-api.ts b/packages/tailwindcss/src/plugin-api.ts index becaf4a40d26..89666988776f 100644 --- a/packages/tailwindcss/src/plugin-api.ts +++ b/packages/tailwindcss/src/plugin-api.ts @@ -24,7 +24,10 @@ export type PluginAPI = { addBase(base: CssInJs): void addVariant(name: string, variant: string | string[] | CssInJs): void - addUtilities(utilities: Record | Record[], options?: {}): void + addUtilities( + utilities: Record | Record[], + options?: {}, + ): void matchUtilities( utilities: Record< string, @@ -115,7 +118,8 @@ function buildPluginApi( } utils[className] ??= [] - utils[className].push(css) + css = Array.isArray(css) ? css : [css] + utils[className].push(...css) } for (let [name, css] of Object.entries(utils)) { From 248851f18a530f811f26c2921f2cb4e32534f8e1 Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Thu, 22 Aug 2024 13:41:42 +0200 Subject: [PATCH 22/25] Add integration tests for @tailwindcss/typography and @tailwindcss/forms --- integrations/cli/plugins.test.ts | 78 ++++++++++++++++++++++++++++++++ integrations/utils.ts | 13 ++++++ 2 files changed, 91 insertions(+) create mode 100644 integrations/cli/plugins.test.ts diff --git a/integrations/cli/plugins.test.ts b/integrations/cli/plugins.test.ts new file mode 100644 index 000000000000..9a17d6dbf6f9 --- /dev/null +++ b/integrations/cli/plugins.test.ts @@ -0,0 +1,78 @@ +import { candidate, css, html, json, test } from '../utils' + +test( + 'builds the typography plugin utilities', + { + fs: { + 'package.json': json` + { + "dependencies": { + "@tailwindcss/typography": "^0.5.14", + "tailwindcss": "workspace:^", + "@tailwindcss/cli": "workspace:^" + } + } + `, + 'index.html': html` +
+

Headline

+

+ Until now, trying to style an article, document, or blog post with Tailwind has been a + tedious task that required a keen eye for typography and a lot of complex custom CSS. +

+
+ `, + 'src/index.css': css` + @import 'tailwindcss'; + @plugin '@tailwindcss/typography'; + `, + }, + }, + async ({ fs, exec }) => { + await exec('pnpm tailwindcss --input src/index.css --output dist/out.css') + + await fs.expectFileToContain('dist/out.css', [ + candidate`prose`, + ':where(h1):not(:where([class~="not-prose"],[class~="not-prose"] *))', + ':where(tbody td, tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *))', + ]) + }, +) + +test( + 'builds the forms plugin utilities', + { + fs: { + 'package.json': json` + { + "dependencies": { + "@tailwindcss/forms": "^0.5.7", + "tailwindcss": "workspace:^", + "@tailwindcss/cli": "workspace:^" + } + } + `, + 'index.html': html` + + + `, + 'src/index.css': css` + @import 'tailwindcss'; + @plugin '@tailwindcss/forms'; + `, + }, + }, + async ({ fs, exec }) => { + await exec('pnpm tailwindcss --input src/index.css --output dist/out.css') + + await fs.expectFileToContain('dist/out.css', [ + // + candidate`form-input`, + candidate`form-textarea`, + ]) + await fs.expectFileNotToContain('dist/out.css', [ + // + candidate`form-radio`, + ]) + }, +) diff --git a/integrations/utils.ts b/integrations/utils.ts index 144b69e1e8fb..71c9eec02abb 100644 --- a/integrations/utils.ts +++ b/integrations/utils.ts @@ -38,6 +38,7 @@ interface TestContext { read(filePath: string): Promise glob(pattern: string): Promise<[string, string][]> expectFileToContain(filePath: string, contents: string | string[]): Promise + expectFileNotToContain(filePath: string, contents: string | string[]): Promise } } type TestCallback = (context: TestContext) => Promise | void @@ -289,9 +290,21 @@ export function test( } }) }, + async expectFileNotToContain(filePath, contents) { + return retryAssertion(async () => { + let fileContent = await this.read(filePath) + for (let content of contents) { + expect(fileContent).not.toContain(content) + } + }) + }, }, } satisfies TestContext + config.fs['.gitignore'] ??= txt` + node_modules/ + ` + for (let [filename, content] of Object.entries(config.fs)) { await context.fs.write(filename, content) } From 59de5aeb7a4ce18f21e7e0c40d94a20cb88d9b32 Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Thu, 22 Aug 2024 13:44:56 +0200 Subject: [PATCH 23/25] Undo changes to accentColor and fill in createDefaultConfig --- .../src/compat/config/create-compat-config.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/tailwindcss/src/compat/config/create-compat-config.ts b/packages/tailwindcss/src/compat/config/create-compat-config.ts index 84801d0f986d..540886f0d008 100644 --- a/packages/tailwindcss/src/compat/config/create-compat-config.ts +++ b/packages/tailwindcss/src/compat/config/create-compat-config.ts @@ -46,10 +46,7 @@ export function createCompatConfig(theme: Theme): UserConfig { return { theme: { colors: ({ theme }) => theme('color', {}), - accentColor: ({ theme }) => ({ - ...theme('colors'), - auto: 'auto', - }), + accentColor: ({ theme }) => theme('colors'), aspectRatio: bareValues((value) => { if (value.fraction === null) return let [lhs, rhs] = segment(value.fraction, '/') @@ -106,10 +103,7 @@ export function createCompatConfig(theme: Theme): UserConfig { ...theme('borderWidth'), ...barePixels, }), - fill: ({ theme }) => ({ - none: 'none', - ...theme('colors'), - }), + fill: ({ theme }) => theme('colors'), flexBasis: ({ theme }) => theme('spacing'), flexGrow: bareIntegers, flexShrink: bareIntegers, From 1c7e429459afd2bfa74dd600fb27d60ef4982557 Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Thu, 22 Aug 2024 13:47:19 +0200 Subject: [PATCH 24/25] Revert changes to Vite playground --- playgrounds/vite/package.json | 2 - playgrounds/vite/src/app.css | 2 - playgrounds/vite/src/app.tsx | 252 +--------------------------------- pnpm-lock.yaml | 73 +--------- 4 files changed, 7 insertions(+), 322 deletions(-) diff --git a/playgrounds/vite/package.json b/playgrounds/vite/package.json index 92e37608b307..561ef06d33f5 100644 --- a/playgrounds/vite/package.json +++ b/playgrounds/vite/package.json @@ -16,8 +16,6 @@ "tailwindcss": "workspace:^" }, "devDependencies": { - "@tailwindcss/forms": "^0.5.7", - "@tailwindcss/typography": "^0.5.14", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "bun": "^1.1.22", diff --git a/playgrounds/vite/src/app.css b/playgrounds/vite/src/app.css index e46d27063fc7..adbd8e19f9b0 100644 --- a/playgrounds/vite/src/app.css +++ b/playgrounds/vite/src/app.css @@ -1,4 +1,2 @@ @import 'tailwindcss'; @plugin "./plugin.js"; -@plugin "./typography.js"; -@plugin "./forms.js"; diff --git a/playgrounds/vite/src/app.tsx b/playgrounds/vite/src/app.tsx index 74db3b1528ab..285bd41439de 100644 --- a/playgrounds/vite/src/app.tsx +++ b/playgrounds/vite/src/app.tsx @@ -2,254 +2,10 @@ import { Foo } from './foo' export function App() { return ( -
-
-

Hello World

- - -
-
-

Headline

-

- Until now, trying to style an article, document, or blog post with Tailwind has been a - tedious task that required a keen eye for typography and a lot of complex custom CSS. -

-

- By default, Tailwind removes all of the default browser styling from paragraphs, headings, - lists and more. This ends up being really useful for building application UIs because you - spend less time undoing user-agent styles, but when you really are just trying to - style some content that came from a rich-text editor in a CMS or a markdown file, it can - be surprising and unintuitive. -

-

- We get lots of complaints about it actually, with people regularly asking us things like: -

-
-

- Why is Tailwind removing the default styles on my h1 elements? How do I - disable this? What do you mean I lose all the other base styles too? -

-
-
-
-
-
-

Reset styles

-

- These are form elements this plugin styles by default. -

-
-
- - - - - - - - - - - -
-
- - - - - - - -
- Checkboxes -
-
- -
-
- -
-
- -
-
-
-
- Radio Buttons -
-
- -
-
- -
-
- -
-
-
-
-
-
-
-

Untouched

-

- These are form elements we don't handle (yet?), but we use this to make sure we - haven't accidentally styled them by mistake. -

-
-
- - - - -
-
-
-
-
+
+

Hello World

+ +
) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 10215e2f3cb6..a916100c7276 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -302,12 +302,6 @@ importers: specifier: workspace:^ version: link:../../packages/tailwindcss devDependencies: - '@tailwindcss/forms': - specifier: ^0.5.7 - version: 0.5.7(tailwindcss@packages+tailwindcss) - '@tailwindcss/typography': - specifier: ^0.5.14 - version: 0.5.14(tailwindcss@packages+tailwindcss) '@types/react': specifier: ^18.3.3 version: 18.3.3 @@ -1040,16 +1034,6 @@ packages: '@swc/helpers@0.5.2': resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} - '@tailwindcss/forms@0.5.7': - resolution: {integrity: sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==} - peerDependencies: - tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1' - - '@tailwindcss/typography@0.5.14': - resolution: {integrity: sha512-ZvOCjUbsJBjL9CxQBn+VEnFpouzuKhxh2dH8xMIWHILL+HfOYtlAkWcyoon8LlzE53d2Yo6YO6pahKKNW3q1YQ==} - peerDependencies: - tailwindcss: '>=3.0.0 || insiders' - '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1265,7 +1249,6 @@ packages: bun@1.1.22: resolution: {integrity: sha512-G2HCPhzhjDc2jEDkZsO9vwPlpHrTm7a8UVwx9oNS5bZqo5OcSK5GPuWYDWjj7+37bRk5OVLfeIvUMtSrbKeIjQ==} - cpu: [arm64, x64] os: [darwin, linux, win32] hasBin: true @@ -1348,11 +1331,6 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -2098,12 +2076,6 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash.castarray@4.4.0: - resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} - - lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -2148,10 +2120,6 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} - mini-svg-data-uri@1.4.4: - resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} - hasBin: true - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -2384,10 +2352,6 @@ packages: yaml: optional: true - postcss-selector-parser@6.0.10: - resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} - engines: {node: '>=4'} - postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -2835,9 +2799,6 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite-node@2.0.5: resolution: {integrity: sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3479,19 +3440,6 @@ snapshots: dependencies: tslib: 2.6.3 - '@tailwindcss/forms@0.5.7(tailwindcss@packages+tailwindcss)': - dependencies: - mini-svg-data-uri: 1.4.4 - tailwindcss: link:packages/tailwindcss - - '@tailwindcss/typography@0.5.14(tailwindcss@packages+tailwindcss)': - dependencies: - lodash.castarray: 4.4.0 - lodash.isplainobject: 4.0.6 - lodash.merge: 4.6.2 - postcss-selector-parser: 6.0.10 - tailwindcss: link:packages/tailwindcss - '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.25.3 @@ -3862,8 +3810,6 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - cssesc@3.0.0: {} - csstype@3.1.3: {} damerau-levenshtein@1.0.8: {} @@ -4131,7 +4077,7 @@ snapshots: eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -4155,7 +4101,7 @@ snapshots: enhanced-resolve: 5.17.1 eslint: 8.57.0 eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.6 is-core-module: 2.15.0 @@ -4177,7 +4123,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -4797,10 +4743,6 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash.castarray@4.4.0: {} - - lodash.isplainobject@4.0.6: {} - lodash.merge@4.6.2: {} lodash.sortby@4.7.0: {} @@ -4838,8 +4780,6 @@ snapshots: mimic-fn@4.0.0: {} - mini-svg-data-uri@1.4.4: {} - minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -5047,11 +4987,6 @@ snapshots: optionalDependencies: postcss: 8.4.41 - postcss-selector-parser@6.0.10: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - postcss-value-parser@4.2.0: {} postcss@8.4.31: @@ -5527,8 +5462,6 @@ snapshots: dependencies: punycode: 2.3.1 - util-deprecate@1.0.2: {} - vite-node@2.0.5(@types/node@20.14.13)(lightningcss@1.26.0): dependencies: cac: 6.7.14 From 8bfd7c7cec7d5db3640310ee04b054701484bf70 Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Thu, 22 Aug 2024 13:52:38 +0200 Subject: [PATCH 25/25] Add change log entries --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffef8f9702fa..e4e8eab5b5d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add support for `addBase` plugins using the `@plugin` directive ([#14172](https://github.com/tailwindlabs/tailwindcss/pull/14172)) - Add support for the `tailwindcss/plugin` export ([#14173](https://github.com/tailwindlabs/tailwindcss/pull/14173)) - Add support for the `theme()` function in plugins ([#14207](https://github.com/tailwindlabs/tailwindcss/pull/14207)) +- Add support for `addComponents`, `matchComponents`, `prefix` plugin APIs ([#14221](https://github.com/tailwindlabs/tailwindcss/pull/14221)) +- Add support for `tailwindcss/colors` and `tailwindcss/defaultTheme` exports for use with plugins ([#14221](https://github.com/tailwindlabs/tailwindcss/pull/14221)) +- Add support for the `@tailwindcss/typography` and `@tailwindcss/forms` plugins ([#14221](https://github.com/tailwindlabs/tailwindcss/pull/14221)) ### Fixed