Skip to content

Commit

Permalink
feat: arbitrary variants
Browse files Browse the repository at this point in the history
  • Loading branch information
sastan committed Oct 3, 2022
1 parent 226f38d commit a3b1bcb
Show file tree
Hide file tree
Showing 11 changed files with 60 additions and 21 deletions.
6 changes: 6 additions & 0 deletions .changeset/itchy-fishes-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@twind/preset-tailwind': patch
'twind': patch
---

Arbitrary variants
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"name": "@twind/preset-tailwind",
"path": "packages/preset-tailwind/dist/preset-tailwind.esnext.js",
"brotli": true,
"limit": "9kb",
"limit": "9.1kb",
"ignore": [
"twind"
]
Expand Down
4 changes: 3 additions & 1 deletion packages/preset-ext/src/variants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { Variant } from 'twind'

import { normalize } from 'twind'

const variants: Variant[] = [
['hocus', '&:hover,&:focus-visible'],
[
Expand Down Expand Up @@ -39,7 +41,7 @@ const variants: Variant[] = [
// - `&>span:underline`
// - `&>*:underline`
// - `&_span:underline` -> `&_span:underline span`
[/&/, (match) => match.input.replace(/_/g, ' ')],
[/&/, (match) => normalize(match.input)],
]

export default variants
11 changes: 10 additions & 1 deletion packages/preset-tailwind/src/rules.test.json
Original file line number Diff line number Diff line change
Expand Up @@ -2026,5 +2026,14 @@
"contrast-more:bg-black dark:bg-white": [
"@media (prefers-contrast:more){.contrast-more\\:bg-black{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity))}}",
"@media (prefers-color-scheme:dark){.dark\\:bg-white{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}}"
]
],
"[&>*]:underline": ".\\[\\&\\>\\*\\]\\:underline>*{text-decoration-line:underline}",
"[.a.b_&]:underline": ".a.b .\\[\\.a\\.b_\\&\\]\\:underline{text-decoration-line:underline}",
"dark:lg:hover:[&>*]:underline": "@media (prefers-color-scheme:dark){@media (min-width:1024px){.dark\\:lg\\:hover\\:\\[\\&\\>\\*\\]\\:underline:hover>*{text-decoration-line:underline}}}",
"[&>*]:!underline": ".\\[\\&\\>\\*\\]\\:\\!underline>*{text-decoration-line:underline !important}",
"[@supports(what:ever)]:underline": "@supports(what:ever){.\\[\\@supports\\(what\\:ever\\)\\]\\:underline{text-decoration-line:underline}}",
"[@media_screen{@media(hover:hover)}]:underline": "@media screen{@media(hover:hover){.\\[\\@media_screen\\{\\@media\\(hover\\:hover\\)\\}\\]\\:underline{text-decoration-line:underline}}}",
"[@media_screen{@media(hover:hover){&:hover}}]:underline": "@media screen{@media(hover:hover){.\\[\\@media_screen\\{\\@media\\(hover\\:hover\\)\\{\\&\\:hover\\}\\}\\]\\:underline:hover{text-decoration-line:underline}}}",
"[&[data-open]]:underline": ".\\[\\&\\[data-open\\]\\]\\:underline[data-open]{text-decoration-line:underline}",
"[&[data-foo][data-bar]:not([data-baz])]:underline": ".\\[\\&\\[data-foo\\]\\[data-bar\\]\\:not\\(\\[data-baz\\]\\)\\]\\:underline[data-foo][data-bar]:not([data-baz]){text-decoration-line:underline}"
}
14 changes: 14 additions & 0 deletions packages/preset-tailwind/src/rules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,17 @@ test('group and peer hashed marker classes', () => {
assert.strictEqual(tw('peer~name[disabled]:underline'), '#1tsl4h5')
assert.deepEqual(tw.target, ['.\\#1krcwoi[disabled]~.\\#1tsl4h5{text-decoration-line:underline}'])
})

test('arbitrary variants with @apply', () => {
const style = css({
'@apply': '[@media_screen{@media(hover:hover){&:hover}}]:underline',
})

assert.strictEqual(style, 'css#itthsz')

assert.strictEqual(tw(style), 'css#itthsz')

assert.deepEqual(tw.target, [
'@media screen{@media(hover:hover){.css\\#itthsz:hover{text-decoration-line:underline}}}',
])
})
5 changes: 5 additions & 0 deletions packages/preset-tailwind/src/variants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Variant } from 'twind'
import type { TailwindTheme } from './types'

import { normalize } from 'twind'

const variants: Variant<TailwindTheme>[] = [
['sticky', '@supports ((position: -webkit-sticky) or (position:sticky))'],
['motion-reduce', '@media (prefers-reduced-motion:reduce)'],
Expand Down Expand Up @@ -40,6 +42,9 @@ const variants: Variant<TailwindTheme>[] = [

// direction variants
['(ltr|rtl)', ({ 1: $1 }) => `[dir="${$1}"] &`],

// Arbitrary variants
[/^\[(.+)]$/, ({ 1: $1 }) => /[&@]/.test($1) && normalize($1).replace(/[}]+$/, '').split('{')],
]

export default variants
2 changes: 1 addition & 1 deletion packages/preset-typography/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ export default function presetTypography({
? ({
'@layer defaults': {
'&': properties,
[context.v('dark')]: darkProperties,
[context.v('dark') as string]: darkProperties,
} as CSSNested,
} as CSSObject)
: undefined
Expand Down
2 changes: 1 addition & 1 deletion packages/twind/src/internal/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function createContext<Theme extends BaseTheme = BaseTheme>({
ignorelist,
}: TwindConfig<Theme>): Context<Theme> {
// Used to cache resolved rule values
const variantCache = new Map<string, string>()
const variantCache = new Map<string, MaybeArray<string>>()

// lazy created resolve functions
const variantResolvers = new Map<Variant<Theme>, VariantFunction<Theme>>()
Expand Down
24 changes: 12 additions & 12 deletions packages/twind/src/internal/precedence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ These are calculated by serialize and added afterwards:
| bits | trait |
| ---- | ----------------------------------- |
| 4 | number of selectors (descending) |
| 4 | number of selectors (descending) |
| 4 | number of declarations (descending) |
| 4 | greatest precedence of properties |
Expand Down Expand Up @@ -305,17 +305,17 @@ export function convert<Theme extends BaseTheme = BaseTheme>(
for (const variant of variants) {
const screen = context.theme('screens', variant)

const condition = (screen && mql(screen)) || context.v(variant)

conditions.push(condition)

precedence |= screen
? (1 << 26) /* Shifts.screens */ | atRulePrecedence(condition)
: variant == 'dark'
? 1 << 30 /* Shifts.darkMode */
: condition[0] == '@'
? atRulePrecedence(condition)
: pseudoPrecedence(condition)
for (const condition of asArray((screen && mql(screen)) || context.v(variant))) {
conditions.push(condition)

precedence |= screen
? (1 << 26) /* Shifts.screens */ | atRulePrecedence(condition)
: variant == 'dark'
? 1 << 30 /* Shifts.darkMode */
: condition[0] == '@'
? atRulePrecedence(condition)
: pseudoPrecedence(condition)
}
}

return { n: name, p: precedence, r: conditions, i: important }
Expand Down
7 changes: 5 additions & 2 deletions packages/twind/src/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,10 @@ export function colorFromTheme<

properties = {
'&': properties,
[context.v('dark')]: create(match as ThemeMatchResult<ColorFromThemeValue>, context),
[context.v('dark') as string]: create(
match as ThemeMatchResult<ColorFromThemeValue>,
context,
),
} as CSSObject
}
}
Expand Down Expand Up @@ -254,7 +257,7 @@ function camelize(value: string): string {
return value.replace(/-./g, (x) => x[1].toUpperCase())
}

function normalize(value: string): string {
export function normalize(value: string): string {
// Keep raw strings if it starts with `url(`
if (value.includes('url(')) {
return value.replace(
Expand Down
4 changes: 2 additions & 2 deletions packages/twind/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export interface Context<Theme extends BaseTheme = BaseTheme> {
*
* @private
*/
v: (value: string) => string
v: (value: string) => MaybeArray<string>

/**
* resolves a rule
Expand Down Expand Up @@ -198,7 +198,7 @@ export type Rule<Theme extends BaseTheme = BaseTheme> =
convert: MatchConverter<Theme>,
]

export type VariantResult = string | Falsey
export type VariantResult = MaybeArray<string> | Falsey

export type VariantResolver<Theme extends BaseTheme = BaseTheme> = (
match: MatchResult,
Expand Down

0 comments on commit a3b1bcb

Please sign in to comment.