diff --git a/CHANGELOG.md b/CHANGELOG.md index a99588d39..3c95f8eaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Added + +- Add `line-height` modifier support to `font-size` utilities ([#9875](https://github.com/tailwindlabs/tailwindcss/pull/9875)) +- Support using variables as arbitrary values without `var(...)` ([#9880](https://github.com/tailwindlabs/tailwindcss/pull/9880), [#9962](https://github.com/tailwindlabs/tailwindcss/pull/9962)) +- Add `delay-0` and `duration-0` by default ([#10294](https://github.com/tailwindlabs/tailwindcss/pull/10294)) +- Add logical properties support for inline direction ([#10166](https://github.com/tailwindlabs/tailwindcss/pull/10166)) +- Add `hyphens` utilities ([#10071](https://github.com/tailwindlabs/tailwindcss/pull/10071)) +- [Oxide] Use `lightningcss` for nesting and vendor prefixes in PostCSS plugin ([#10399](https://github.com/tailwindlabs/tailwindcss/pull/10399)) +- Add support for configuring default `font-variation-settings` for a `font-family` ([#10034](https://github.com/tailwindlabs/tailwindcss/pull/10034), [#10515](https://github.com/tailwindlabs/tailwindcss/pull/10515)) +- Add `caption-side` utilities ([#10470](https://github.com/tailwindlabs/tailwindcss/pull/10470)) ## [3.2.5] - 2023-02-08 diff --git a/src/corePlugins.js b/src/corePlugins.js index 18c06eb08..e440eff8e 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -1,6 +1,7 @@ import fs from 'fs' import * as path from 'path' import postcss from 'postcss' +import { env } from './lib/sharedState' import createUtilityPlugin from './util/createUtilityPlugin' import buildMediaQuery from './util/buildMediaQuery' import escapeClassName from './util/escapeClassName' @@ -646,6 +647,8 @@ export let corePlugins = { ['inset-y', ['top', 'bottom']], ], [ + ['start', ['inset-inline-start']], + ['end', ['inset-inline-end']], ['top', ['top']], ['right', ['right']], ['bottom', ['bottom']], @@ -697,6 +700,8 @@ export let corePlugins = { ['my', ['margin-top', 'margin-bottom']], ], [ + ['ms', ['margin-inline-start']], + ['me', ['margin-inline-end']], ['mt', ['margin-top']], ['mr', ['margin-right']], ['mb', ['margin-bottom']], @@ -767,6 +772,13 @@ export let corePlugins = { }) }, + captionSide: ({ addUtilities }) => { + addUtilities({ + '.caption-top': { 'caption-side': 'top' }, + '.caption-bottom': { 'caption-side': 'bottom' }, + }) + }, + borderCollapse: ({ addUtilities }) => { addUtilities({ '.border-collapse': { 'border-collapse': 'collapse' }, @@ -1045,6 +1057,8 @@ export let corePlugins = { ['scroll-my', ['scroll-margin-top', 'scroll-margin-bottom']], ], [ + ['scroll-ms', ['scroll-margin-inline-start']], + ['scroll-me', ['scroll-margin-inline-end']], ['scroll-mt', ['scroll-margin-top']], ['scroll-mr', ['scroll-margin-right']], ['scroll-mb', ['scroll-margin-bottom']], @@ -1061,6 +1075,8 @@ export let corePlugins = { ['scroll-py', ['scroll-padding-top', 'scroll-padding-bottom']], ], [ + ['scroll-ps', ['scroll-padding-inline-start']], + ['scroll-pe', ['scroll-padding-inline-end']], ['scroll-pt', ['scroll-padding-top']], ['scroll-pr', ['scroll-padding-right']], ['scroll-pb', ['scroll-padding-bottom']], @@ -1234,6 +1250,16 @@ export let corePlugins = { 'space-x': (value) => { value = value === '0' ? '0px' : value + if (env.OXIDE) { + return { + '& > :not([hidden]) ~ :not([hidden])': { + '--tw-space-x-reverse': '0', + 'margin-inline-end': `calc(${value} * var(--tw-space-x-reverse))`, + 'margin-inline-start': `calc(${value} * calc(1 - var(--tw-space-x-reverse)))`, + }, + } + } + return { '& > :not([hidden]) ~ :not([hidden])': { '--tw-space-x-reverse': '0', @@ -1269,6 +1295,17 @@ export let corePlugins = { 'divide-x': (value) => { value = value === '0' ? '0px' : value + if (env.OXIDE) { + return { + '& > :not([hidden]) ~ :not([hidden])': { + '@defaults border-width': {}, + '--tw-divide-x-reverse': '0', + 'border-inline-end-width': `calc(${value} * var(--tw-divide-x-reverse))`, + 'border-inline-start-width': `calc(${value} * calc(1 - var(--tw-divide-x-reverse)))`, + }, + } + } + return { '& > :not([hidden]) ~ :not([hidden])': { '@defaults border-width': {}, @@ -1436,6 +1473,14 @@ export let corePlugins = { }) }, + hyphens: ({ addUtilities }) => { + addUtilities({ + '.hyphens-none': { hyphens: 'none' }, + '.hyphens-manual': { hyphens: 'manual' }, + '.hyphens-auto': { hyphens: 'auto' }, + }) + }, + whitespace: ({ addUtilities }) => { addUtilities({ '.whitespace-normal': { 'white-space': 'normal' }, @@ -1458,12 +1503,18 @@ export let corePlugins = { borderRadius: createUtilityPlugin('borderRadius', [ ['rounded', ['border-radius']], [ + ['rounded-s', ['border-start-start-radius', 'border-end-start-radius']], + ['rounded-e', ['border-start-end-radius', 'border-end-end-radius']], ['rounded-t', ['border-top-left-radius', 'border-top-right-radius']], ['rounded-r', ['border-top-right-radius', 'border-bottom-right-radius']], ['rounded-b', ['border-bottom-right-radius', 'border-bottom-left-radius']], ['rounded-l', ['border-top-left-radius', 'border-bottom-left-radius']], ], [ + ['rounded-ss', ['border-start-start-radius']], + ['rounded-se', ['border-start-end-radius']], + ['rounded-ee', ['border-end-end-radius']], + ['rounded-es', ['border-end-start-radius']], ['rounded-tl', ['border-top-left-radius']], ['rounded-tr', ['border-top-right-radius']], ['rounded-br', ['border-bottom-right-radius']], @@ -1480,6 +1531,8 @@ export let corePlugins = { ['border-y', [['@defaults border-width', {}], 'border-top-width', 'border-bottom-width']], ], [ + ['border-s', [['@defaults border-width', {}], 'border-inline-start-width']], + ['border-e', [['@defaults border-width', {}], 'border-inline-end-width']], ['border-t', [['@defaults border-width', {}], 'border-top-width']], ['border-r', [['@defaults border-width', {}], 'border-right-width']], ['border-b', [['@defaults border-width', {}], 'border-bottom-width']], @@ -1562,6 +1615,32 @@ export let corePlugins = { matchUtilities( { + 'border-s': (value) => { + if (!corePlugins('borderOpacity')) { + return { + 'border-inline-start-color': toColorValue(value), + } + } + + return withAlphaVariable({ + color: value, + property: 'border-inline-start-color', + variable: '--tw-border-opacity', + }) + }, + 'border-e': (value) => { + if (!corePlugins('borderOpacity')) { + return { + 'border-inline-end-color': toColorValue(value), + } + } + + return withAlphaVariable({ + color: value, + property: 'border-inline-end-color', + variable: '--tw-border-opacity', + }) + }, 'border-t': (value) => { if (!corePlugins('borderOpacity')) { return { @@ -1798,6 +1877,8 @@ export let corePlugins = { ['py', ['padding-top', 'padding-bottom']], ], [ + ['ps', ['padding-inline-start']], + ['pe', ['padding-inline-end']], ['pt', ['padding-top']], ['pr', ['padding-right']], ['pb', ['padding-bottom']], @@ -1841,13 +1922,16 @@ export let corePlugins = { font: (value) => { let [families, options = {}] = Array.isArray(value) && isPlainObject(value[1]) ? value : [value] - let { fontFeatureSettings } = options + let { fontFeatureSettings, fontVariationSettings } = options return { 'font-family': Array.isArray(families) ? families.join(', ') : families, ...(fontFeatureSettings === undefined ? {} : { 'font-feature-settings': fontFeatureSettings }), + ...(fontVariationSettings === undefined + ? {} + : { 'font-variation-settings': fontVariationSettings }), } }, }, @@ -1861,9 +1945,16 @@ export let corePlugins = { fontSize: ({ matchUtilities, theme }) => { matchUtilities( { - text: (value) => { + text: (value, { modifier }) => { let [fontSize, options] = Array.isArray(value) ? value : [value] + if (modifier) { + return { + 'font-size': fontSize, + 'line-height': modifier, + } + } + let { lineHeight, letterSpacing, fontWeight } = isPlainObject(options) ? options : { lineHeight: options } @@ -1878,6 +1969,7 @@ export let corePlugins = { }, { values: theme('fontSize'), + modifiers: theme('lineHeight'), type: ['absolute-size', 'relative-size', 'length', 'percentage'], } ) diff --git a/src/css/preflight.css b/src/css/preflight.css index 302904429..fab875d95 100644 --- a/src/css/preflight.css +++ b/src/css/preflight.css @@ -23,6 +23,7 @@ 3. Use a more readable tab size. 4. Use the user's configured `sans` font-family by default. 5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. */ html { @@ -32,6 +33,7 @@ html { tab-size: 4; /* 3 */ font-family: theme('fontFamily.sans', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); /* 4 */ font-feature-settings: theme('fontFamily.sans[1].fontFeatureSettings', normal); /* 5 */ + font-variation-settings: theme('fontFamily.sans[1].fontVariationSettings', normal); /* 6 */ } /* diff --git a/src/util/dataTypes.js b/src/util/dataTypes.js index 5005f5fe0..55cc00579 100644 --- a/src/util/dataTypes.js +++ b/src/util/dataTypes.js @@ -16,6 +16,10 @@ const placeholderRe = new RegExp(placeholder, 'g') // This is not a data type, but rather a function that can normalize the // correct values. export function normalize(value, isRoot = true) { + if (value.startsWith('--')) { + return `var(${value})` + } + // Keep raw strings if it starts with `url(` if (value.includes('url(')) { return value diff --git a/src/util/pluginUtils.js b/src/util/pluginUtils.js index 5c4821d3c..e347f404b 100644 --- a/src/util/pluginUtils.js +++ b/src/util/pluginUtils.js @@ -133,6 +133,14 @@ export function parseColorFormat(value) { return value } +function unwrapArbitraryModifier(modifier) { + modifier = modifier.slice(1, -1) + if (modifier.startsWith('--')) { + modifier = `var(${modifier})` + } + return modifier +} + export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) { if (options.values?.[modifier] !== undefined) { return parseColorFormat(options.values?.[modifier]) @@ -153,7 +161,7 @@ export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) { normalizedColor = parseColorFormat(normalizedColor) if (isArbitraryValue(alpha)) { - return withAlphaValue(normalizedColor, alpha.slice(1, -1)) + return withAlphaValue(normalizedColor, unwrapArbitraryModifier(alpha)) } if (tailwindConfig.theme?.opacity?.[alpha] === undefined) { @@ -287,7 +295,7 @@ export function* getMatchingTypes(types, rawModifier, options, tailwindConfig) { if (configValue !== null) { utilityModifier = configValue } else if (isArbitraryValue(utilityModifier)) { - utilityModifier = utilityModifier.slice(1, -1) + utilityModifier = unwrapArbitraryModifier(utilityModifier) } } } diff --git a/stubs/defaultConfig.stub.js b/stubs/defaultConfig.stub.js index 21a82cd48..8a3fac7a3 100644 --- a/stubs/defaultConfig.stub.js +++ b/stubs/defaultConfig.stub.js @@ -853,6 +853,7 @@ module.exports = { 'top-left': 'top left', }, transitionDelay: { + 0: '0s', 75: '75ms', 100: '100ms', 150: '150ms', @@ -864,6 +865,7 @@ module.exports = { }, transitionDuration: { DEFAULT: '150ms', + 0: '0s', 75: '75ms', 100: '100ms', 150: '150ms', diff --git a/tests/any-type.test.js b/tests/any-type.test.js index 8593362aa..add8a1795 100644 --- a/tests/any-type.test.js +++ b/tests/any-type.test.js @@ -428,8 +428,8 @@ crosscheck(({ stable, oxide }) => { } .space-x-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; - margin-right: calc(var(--any-value) * var(--tw-space-x-reverse)); - margin-left: calc(var(--any-value) * calc(1 - var(--tw-space-x-reverse))); + margin-inline-start: calc(var(--any-value) * calc(1 - var(--tw-space-x-reverse))); + margin-inline-end: calc(var(--any-value) * var(--tw-space-x-reverse)); } .space-y-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; diff --git a/tests/arbitrary-values.oxide.test.css b/tests/arbitrary-values.oxide.test.css index e19212d74..acd68ee33 100644 --- a/tests/arbitrary-values.oxide.test.css +++ b/tests/arbitrary-values.oxide.test.css @@ -455,13 +455,13 @@ } .space-x-\[20cm\] > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; - margin-right: calc(20cm * var(--tw-space-x-reverse)); - margin-left: calc(20cm * calc(1 - var(--tw-space-x-reverse))); + margin-inline-start: calc(20cm * calc(1 - var(--tw-space-x-reverse))); + margin-inline-end: calc(20cm * var(--tw-space-x-reverse)); } .space-x-\[calc\(20\%-1cm\)\] > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; - margin-right: calc(calc(20% - 1cm) * var(--tw-space-x-reverse)); - margin-left: calc(calc(20% - 1cm) * calc(1 - var(--tw-space-x-reverse))); + margin-inline-start: calc(calc(20% - 1cm) * calc(1 - var(--tw-space-x-reverse))); + margin-inline-end: calc(calc(20% - 1cm) * var(--tw-space-x-reverse)); } .space-y-\[20cm\] > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; @@ -475,13 +475,13 @@ } .divide-x-\[20cm\] > :not([hidden]) ~ :not([hidden]) { --tw-divide-x-reverse: 0; - border-right-width: calc(20cm * var(--tw-divide-x-reverse)); - border-left-width: calc(20cm * calc(1 - var(--tw-divide-x-reverse))); + border-inline-end-width: calc(20cm * var(--tw-divide-x-reverse)); + border-inline-start-width: calc(20cm * calc(1 - var(--tw-divide-x-reverse))); } .divide-x-\[calc\(20\%-1cm\)\] > :not([hidden]) ~ :not([hidden]) { --tw-divide-x-reverse: 0; - border-right-width: calc(calc(20% - 1cm) * var(--tw-divide-x-reverse)); - border-left-width: calc(calc(20% - 1cm) * calc(1 - var(--tw-divide-x-reverse))); + border-inline-end-width: calc(calc(20% - 1cm) * var(--tw-divide-x-reverse)); + border-inline-start-width: calc(calc(20% - 1cm) * calc(1 - var(--tw-divide-x-reverse))); } .divide-y-\[20cm\] > :not([hidden]) ~ :not([hidden]) { --tw-divide-y-reverse: 0; diff --git a/tests/arbitrary-values.test.js b/tests/arbitrary-values.test.js index 57686114d..183fcf57c 100644 --- a/tests/arbitrary-values.test.js +++ b/tests/arbitrary-values.test.js @@ -545,4 +545,70 @@ crosscheck(({ stable, oxide }) => { `) }) }) + + it('can use CSS variables as arbitrary values without `var()`', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .w-\[--width-var\] { + width: var(--width-var); + } + .bg-\[--color-var\,\#000\] { + background-color: var(--color-var, #000); + } + .bg-\[--color-var\] { + background-color: var(--color-var); + } + .bg-\[length\:--size-var\] { + background-size: var(--size-var); + } + .text-\[length\:--size-var\,12px\] { + font-size: var(--size-var, 12px); + } + `) + }) + }) + + it('can use CSS variables as arbitrary modifiers without `var()`', () => { + let config = { + content: [ + { + raw: html``, + }, + ], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-red-500\/\[--opacity\] { + background-color: rgb(239 68 68 / var(--opacity)); + } + .text-sm\/\[--line-height\] { + font-size: 0.875rem; + line-height: var(--line-height); + } + `) + }) + }) }) diff --git a/tests/basic-usage.oxide.test.css b/tests/basic-usage.oxide.test.css index 9484dfdd6..5623fe79e 100644 --- a/tests/basic-usage.oxide.test.css +++ b/tests/basic-usage.oxide.test.css @@ -252,6 +252,12 @@ .table-fixed { table-layout: fixed; } +.caption-top { + caption-side: top; +} +.caption-bottom { + caption-side: bottom; +} .border-collapse { border-collapse: collapse; } @@ -470,8 +476,8 @@ } .space-x-4 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; - margin-right: calc(1rem * var(--tw-space-x-reverse)); - margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); + margin-inline-start: calc(1rem * calc(1 - var(--tw-space-x-reverse))); + margin-inline-end: calc(1rem * var(--tw-space-x-reverse)); } .space-y-3 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; @@ -486,13 +492,13 @@ } .divide-x-0 > :not([hidden]) ~ :not([hidden]) { --tw-divide-x-reverse: 0; - border-right-width: calc(0px * var(--tw-divide-x-reverse)); - border-left-width: calc(0px * calc(1 - var(--tw-divide-x-reverse))); + border-inline-end-width: calc(0px * var(--tw-divide-x-reverse)); + border-inline-start-width: calc(0px * calc(1 - var(--tw-divide-x-reverse))); } .divide-x-2 > :not([hidden]) ~ :not([hidden]) { --tw-divide-x-reverse: 0; - border-right-width: calc(2px * var(--tw-divide-x-reverse)); - border-left-width: calc(2px * calc(1 - var(--tw-divide-x-reverse))); + border-inline-end-width: calc(2px * var(--tw-divide-x-reverse)); + border-inline-start-width: calc(2px * calc(1 - var(--tw-divide-x-reverse))); } .divide-y-0 > :not([hidden]) ~ :not([hidden]) { --tw-divide-y-reverse: 0; diff --git a/tests/basic-usage.test.css b/tests/basic-usage.test.css index 9484dfdd6..184ada947 100644 --- a/tests/basic-usage.test.css +++ b/tests/basic-usage.test.css @@ -252,6 +252,12 @@ .table-fixed { table-layout: fixed; } +.caption-top { + caption-side: top; +} +.caption-bottom { + caption-side: bottom; +} .border-collapse { border-collapse: collapse; } diff --git a/tests/basic-usage.test.js b/tests/basic-usage.test.js index 9ec0e5917..4b39eb681 100644 --- a/tests/basic-usage.test.js +++ b/tests/basic-usage.test.js @@ -161,6 +161,8 @@ crosscheck(({ stable, oxide }) => { + + diff --git a/tests/evaluateTailwindFunctions.test.js b/tests/evaluateTailwindFunctions.test.js index 82aa9b7da..5b41a7030 100644 --- a/tests/evaluateTailwindFunctions.test.js +++ b/tests/evaluateTailwindFunctions.test.js @@ -591,6 +591,72 @@ crosscheck(({ stable, oxide }) => { }) }) + test('font-family values are retrieved without font-variation-settings', () => { + let input = css` + .heading-1 { + font-family: theme('fontFamily.sans'); + } + .heading-2 { + font-family: theme('fontFamily.serif'); + } + .heading-3 { + font-family: theme('fontFamily.mono'); + } + ` + + let output = css` + .heading-1 { + font-family: Inter; + } + .heading-2 { + font-family: Times, serif; + } + .heading-3 { + font-family: Menlo, monospace; + } + ` + + return run(input, { + theme: { + fontFamily: { + sans: ['Inter', { fontVariationSettings: '"opsz" 32' }], + serif: [['Times', 'serif'], { fontVariationSettings: '"opsz" 32' }], + mono: ['Menlo, monospace', { fontVariationSettings: '"opsz" 32' }], + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) + }) + + test('font-variation-settings values can be retrieved', () => { + let input = css` + .heading { + font-family: theme('fontFamily.sans'); + font-variation-settings: theme('fontFamily.sans[1].fontVariationSettings'); + } + ` + + let output = css` + .heading { + font-family: Inter; + font-variation-settings: 'opsz' 32; + } + ` + + return run(input, { + theme: { + fontFamily: { + sans: ['Inter', { fontVariationSettings: "'opsz' 32" }], + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) + }) + test('font-family values are joined when an array', () => { let input = css` .element { diff --git a/tests/getClassList.test.js b/tests/getClassList.test.js index 5fd9b00b0..671a767ec 100644 --- a/tests/getClassList.test.js +++ b/tests/getClassList.test.js @@ -88,6 +88,7 @@ crosscheck(() => { let classes = context.getClassList({ includeMetadata: true }) expect(classes).not.toContain('bg-red-500') + expect(classes).not.toContain('text-2xl') expect(classes).toContainEqual([ 'bg-red-500', @@ -111,6 +112,27 @@ crosscheck(() => { ], }, ]) + expect(classes).toContainEqual([ + 'text-2xl', + { + modifiers: [ + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + 'none', + 'tight', + 'snug', + 'normal', + 'relaxed', + 'loose', + ], + }, + ]) }) it('should generate plugin-defined utilities with modifier data when requested', () => { diff --git a/tests/kitchen-sink.test.js b/tests/kitchen-sink.test.js index fbe4fc9df..976018cb0 100644 --- a/tests/kitchen-sink.test.js +++ b/tests/kitchen-sink.test.js @@ -13,7 +13,12 @@ crosscheck(() => { > - + + + + + + @@ -204,7 +209,7 @@ crosscheck(() => { @apply font-bold group-hover:font-normal; } .list { - @apply space-x-4; + @apply space-y-4; } .nested { .example { @@ -319,9 +324,9 @@ crosscheck(() => { font-weight: 400; } .list > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(1rem * var(--tw-space-x-reverse)); - margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); } .nested .example { font-weight: 700; @@ -401,6 +406,29 @@ crosscheck(() => { .foo .bg-black { appearance: none; } + .inset-6 { + inset: 1.5rem; + } + .inset-x-1 { + left: 0.25rem; + right: 0.25rem; + } + .end-8 { + inset-inline-end: 2rem; + } + .start-4 { + inset-inline-start: 1rem; + } + .mx-1 { + margin-left: 0.25rem; + margin-right: 0.25rem; + } + .me-8 { + margin-inline-end: 2rem; + } + .ms-4 { + margin-inline-start: 1rem; + } .mt-6 { margin-top: 1.5rem; } @@ -419,6 +447,41 @@ crosscheck(() => { .grid-cols-\[200px\,repeat\(auto-fill\,minmax\(15\%\,100px\)\)\,300px\] { grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px; } + .rounded-e { + border-start-end-radius: 0.25rem; + border-end-end-radius: 0.25rem; + } + .rounded-s { + border-start-start-radius: 0.25rem; + border-end-start-radius: 0.25rem; + } + .rounded-es { + border-end-start-radius: 0.25rem; + } + .rounded-ss { + border-start-start-radius: 0.25rem; + } + .border-2 { + border-width: 2px; + } + .border-e-4 { + border-inline-end-width: 4px; + } + .border-s-0 { + border-inline-start-width: 0; + } + .border-black { + --tw-border-opacity: 1; + border-color: rgb(0 0 0 / var(--tw-border-opacity)); + } + .border-e-red-400 { + --tw-border-opacity: 1; + border-inline-end-color: rgb(248 113 113 / var(--tw-border-opacity)); + } + .border-s-green-500 { + --tw-border-opacity: 1; + border-inline-start-color: rgb(34 197 94 / var(--tw-border-opacity)); + } .bg-black { --tw-bg-opacity: 1; background-color: rgb(0 0 0 / var(--tw-bg-opacity)); @@ -441,6 +504,19 @@ crosscheck(() => { --tw-gradient-to: #bada5500; --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); } + .px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; + } + .pe-8 { + padding-inline-end: 2rem; + } + .ps-4 { + padding-inline-start: 1rem; + } + .pt-6 { + padding-top: 1.5rem; + } .text-center { text-align: center; } diff --git a/tests/match-utilities.test.js b/tests/match-utilities.test.js index 9f4d1df09..9b3cd4c56 100644 --- a/tests/match-utilities.test.js +++ b/tests/match-utilities.test.js @@ -6,8 +6,8 @@ crosscheck(() => { content: [ { raw: html``, + class="test test/foo test-1/foo test-2/foo test/[foo] test-1/[foo] test-[8]/[9]" + > `, }, ], corePlugins: { preflight: false }, @@ -27,6 +27,7 @@ crosscheck(() => { '1': 'one', '2': 'two', '1/foo': 'onefoo', + '[8]/[9]': 'eightnine', }, modifiers: 'any', } @@ -54,6 +55,9 @@ crosscheck(() => { .test-2\/foo { color: two_foo; } + .test-\[8\]\/\[9\] { + color: eightnine_null; + } .test\/\[foo\] { color: default_[foo]; } diff --git a/tests/oxide.test.js b/tests/oxide.test.js index ee7d68697..d2a697626 100644 --- a/tests/oxide.test.js +++ b/tests/oxide.test.js @@ -13,8 +13,8 @@ crosscheck(({ stable, oxide }) => { ${defaults} .space-x-4 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; - margin-right: calc(1rem * var(--tw-space-x-reverse)); - margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); + margin-inline-start: calc(1rem * calc(1 - var(--tw-space-x-reverse))); + margin-inline-end: calc(1rem * var(--tw-space-x-reverse)); } `) }) diff --git a/tests/plugins/divide.test.js b/tests/plugins/divide.test.js index fe63645db..314233bea 100644 --- a/tests/plugins/divide.test.js +++ b/tests/plugins/divide.test.js @@ -40,8 +40,8 @@ crosscheck(({ stable, oxide }) => { ${defaults} .divide-x > :not([hidden]) ~ :not([hidden]) { --tw-divide-x-reverse: 0; - border-right-width: calc(1px * var(--tw-divide-x-reverse)); - border-left-width: calc(1px * calc(1 - var(--tw-divide-x-reverse))); + border-inline-end-width: calc(1px * var(--tw-divide-x-reverse)); + border-inline-start-width: calc(1px * calc(1 - var(--tw-divide-x-reverse))); } `) }) diff --git a/tests/plugins/fontFamily.test.js b/tests/plugins/fontFamily.test.js index fb025720a..1170dac8e 100644 --- a/tests/plugins/fontFamily.test.js +++ b/tests/plugins/fontFamily.test.js @@ -98,3 +98,43 @@ crosscheck(() => { }) }) }) + +test('font-variation-settings can be provided when families are defined as a string', () => { + let config = { + content: [{ raw: html`` }], + theme: { + fontFamily: { + sans: ['Inter, sans-serif', { fontVariationSettings: '"opsz" 32' }], + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(` + .font-sans { + font-family: Inter, sans-serif; + font-variation-settings: "opsz" 32; + } + `) + }) +}) + +test('font-variation-settings can be provided when families are defined as an array', () => { + let config = { + content: [{ raw: html`` }], + theme: { + fontFamily: { + sans: [['Inter', 'sans-serif'], { fontVariationSettings: '"opsz" 32' }], + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(` + .font-sans { + font-family: Inter, sans-serif; + font-variation-settings: "opsz" 32; + } + `) + }) +}) diff --git a/tests/plugins/fontSize.test.js b/tests/plugins/fontSize.test.js index c3a1159eb..08e88bb76 100644 --- a/tests/plugins/fontSize.test.js +++ b/tests/plugins/fontSize.test.js @@ -120,4 +120,78 @@ crosscheck(() => { `) }) }) + + test('font-size utilities can include a line-height modifier', () => { + let config = { + content: [ + { + raw: html`