Skip to content

Commit

Permalink
feat: support attribute selector variants (#151)
Browse files Browse the repository at this point in the history
* feat: support attribute selector variants

* fix: support negated attribute selectors
  • Loading branch information
sastan committed Mar 20, 2021
1 parent a81e1ef commit ba46fb7
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 9 deletions.
14 changes: 12 additions & 2 deletions src/__tests__/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,6 @@
"text-[#1da1f2]": ".text-\\[\\#1da1f2\\]{--tw-text-opacity:1;color:#1da1f2;color:rgba(29,161,242,var(--tw-text-opacity))}",
"grid-cols-[minmax(100px,max-content)repeat(auto-fill,200px)20%]": ".grid-cols-\\[minmax\\(100px\\,max-content\\)repeat\\(auto-fill\\,200px\\)20\\%\\]{grid-template-columns:minmax(100px,max-content)repeat(auto-fill,200px)20%}",
"grid-cols-[repeat(auto-fit,minmax(150px,1fr))]": ".grid-cols-\\[repeat\\(auto-fit\\,minmax\\(150px\\,1fr\\)\\)\\]{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}",
"grid-rows-auto-1fr-auto": ".grid-rows-auto-1fr-auto{grid-template-rows:auto-1fr-auto}",
"w-[clamp(23ch,50%,46ch)]": ".w-\\[clamp\\(23ch\\,50\\%\\,46ch\\)\\]{width:clamp(23ch,50%,46ch)}",
"bg-[#1da1f2]": ".bg-\\[\\#1da1f2\\]{--tw-bg-opacity:1;background-color:#1da1f2;background-color:rgba(29,161,242,var(--tw-bg-opacity))}",
"translate-x-[-650px]": ".translate-x-\\[-650px\\]{--tw-translate-x:-650px;transform:translateX(-650px);transform:translateX(var(--tw-translate-x,0)) translateY(var(--tw-translate-y,0)) rotate(var(--tw-rotate,0)) skewX(var(--tw-skew-x,0)) skewY(var(--tw-skew-y,0)) scaleX(var(--tw-scale-x,1)) scaleY(var(--tw-scale-y,1))}",
Expand Down Expand Up @@ -869,5 +868,16 @@
"duration-[2s]": ".duration-\\[2s\\]{transition-duration:2s}",
"text-[2.23rem]": ".text-\\[2\\.23rem\\]{font-size:2.23rem}",
"hover:text-[rgba(23,83,43,.8)]": ".hover\\:text-\\[rgba\\(23\\,83\\,43\\,\\.8\\)\\]:hover{color:rgba(23,83,43,.8)}",
"text-[tomato]": ".text-\\[tomato\\]{color:tomato}"
"text-[tomato]": ".text-\\[tomato\\]{color:tomato}",
"[aria-expanded='true']:font-bold": ".\\[aria-expanded\\=\\'true\\'\\]\\:font-bold[aria-expanded='true']{font-weight:700}",
"[data-reach-menu-item][data-selected]:bg-red-300": ".\\[data-reach-menu-item\\]\\[data-selected\\]\\:bg-red-300[data-reach-menu-item][data-selected]{--tw-bg-opacity:1;background-color:#fca5a5;background-color:rgba(252,165,165,var(--tw-bg-opacity))}",
"[href^='#']:bg-yellow-400": ".\\[href\\^\\=\\'\\#\\'\\]\\:bg-yellow-400[href^='#']{--tw-bg-opacity:1;background-color:#fbbf24;background-color:rgba(251,191,36,var(--tw-bg-opacity))}",
"[href*='example']:bg-gray-300": ".\\[href\\*\\=\\'example\\'\\]\\:bg-gray-300[href*='example']{--tw-bg-opacity:1;background-color:#d1d5db;background-color:rgba(209,213,219,var(--tw-bg-opacity))}",
"[href$='.org']:bg-red-400": ".\\[href\\$\\=\\'\\.org\\'\\]\\:bg-red-400[href$='.org']{--tw-bg-opacity:1;background-color:#f87171;background-color:rgba(248,113,113,var(--tw-bg-opacity))}",
"[href^='https'][href$='.org']:bg-green-400": ".\\[href\\^\\=\\'https\\'\\]\\[href\\$\\=\\'\\.org\\'\\]\\:bg-green-400[href^='https'][href$='.org']{--tw-bg-opacity:1;background-color:#34d399;background-color:rgba(52,211,153,var(--tw-bg-opacity))}",
"[lang]:font-bold": ".\\[lang\\]\\:font-bold[lang]{font-weight:700}",
"not-[lang]:italic": ".not-\\[lang\\]\\:italic:not([lang]){font-style:italic}",
"[lang~='en-us']:text-blue-400": ".\\[lang\\~\\=\\'en-us\\'\\]\\:text-blue-400[lang~='en-us']{--tw-text-opacity:1;color:#60a5fa;color:rgba(96,165,250,var(--tw-text-opacity))}",
"[lang|='zh']:text-red-400": ".\\[lang\\|\\=\\'zh\\'\\]\\:text-red-400[lang|='zh']{--tw-text-opacity:1;color:#f87171;color:rgba(248,113,113,var(--tw-text-opacity))}",
"[data-lang='zh-TW']:text-purple-400": ".\\[data-lang\\=\\'zh-TW\\'\\]\\:text-purple-400[data-lang='zh-TW']{--tw-text-opacity:1;color:#a78bfa;color:rgba(167,139,250,var(--tw-text-opacity))}"
}
20 changes: 15 additions & 5 deletions src/twind/configure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,20 @@ import { silent, strict, warn } from './modes'
import { autoprefix, noprefix } from './prefix'
import { makeThemeResolver } from './theme'

import { cyrb32, identity, tail, merge, evalThunk, ensureMaxSize, includes } from '../internal/util'
import {
cyrb32,
identity,
tail,
merge,
evalThunk,
ensureMaxSize,
includes,
join,
} from '../internal/util'

import { parse } from './parse'
import { translate as makeTranslate } from './translate'
import { decorate as makeDecorate } from './decorate'
import { decorate as makeDecorate, prepareVariantSelector } from './decorate'
import { serialize as makeSerialize } from './serialize'
import { inject as makeInject } from './inject'

Expand Down Expand Up @@ -227,6 +236,9 @@ export const configure = (
}

if (translation && typeof translation == 'object') {
rule.v = rule.v.map(prepareVariantSelector)
if (important) rule.i = important

// 3. decorate: apply variants
translation = decorate(translation, rule)

Expand All @@ -243,8 +255,6 @@ export const configure = (

// 4. serialize: convert to css string with precedence
// 5. inject: add to dom
if (important) rule.i = important

serialize(translation, className, rule, layer).forEach(inject)

if (translation._) {
Expand Down Expand Up @@ -282,7 +292,7 @@ export const configure = (
// This function is called from `tw(...)`
// it parses, translates, decorates, serializes and injects the tokens
const process = (tokens: unknown[]): string =>
parse(tokens).map(convert).filter(Boolean).join(' ')
join(parse(tokens).map(convert).filter(Boolean) as string[], ' ')

// Determine if we should inject the preflight (browser normalize)
const preflight = sanitize<Preflight | false | CSSRules>(config.preflight, identity, false)
Expand Down
12 changes: 10 additions & 2 deletions src/twind/decorate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ let _: RegExpExecArray | null | readonly ThemeScreenValue[] | string
export const GROUP_RE = /^:(group(?:(?!-focus).+?)*)-(.+)$/
export const NOT_PREFIX_RE = /^(:not)-(.+)/

export const prepareVariantSelector = (variant: string): string =>
variant[1] == '[' ? tail(variant) : variant

// Wraps a CSS rule object with variant at-rules and pseudo classes
// { '.selector': {...} }
// => { '&:hover': { '.selector': {...} } }
Expand All @@ -25,7 +28,7 @@ export const decorate = (

// Dark mode
if (variant == ':dark' && darkMode == 'class') {
return { [`.dark &`]: translation }
return { '.dark &': translation }
}

// Groups classes like: group-focus and group-hover
Expand All @@ -38,7 +41,12 @@ export const decorate = (
// Check other well known variants
// and fallback to pseudo class or element
return {
[variants[tail(variant)] || '&' + variant.replace(NOT_PREFIX_RE, '$1(:$2)')]: translation,
[variants[tail(variant)] ||
'&' +
variant.replace(
NOT_PREFIX_RE,
(_, not, variant) => not + '(' + prepareVariantSelector(':' + variant) + ')',
)]: translation,
}
}

Expand Down

0 comments on commit ba46fb7

Please sign in to comment.