From 7f72cb0ea61925fe5a9fca067656fd76f7df2f52 Mon Sep 17 00:00:00 2001 From: Sascha Tandel Date: Tue, 13 Dec 2022 08:43:33 +0100 Subject: [PATCH] Add `aria-*` and `data-*` variants --- .changeset/quiet-tips-begin.md | 5 ++ .../src/__snapshots__/enumerate.test.ts.snap | 39 ++++++++++++ .../src/__snapshots__/suggest-at.test.ts.snap | 61 +++++++++++++++++++ .../src/__snapshots__/suggest.test.ts.snap | 12 ++++ packages/intellisense/src/enumerate.test.ts | 11 ++++ packages/intellisense/src/suggest.test.ts | 2 +- packages/preset-tailwind/src/defaultTheme.ts | 4 +- packages/preset-tailwind/src/rules.test.json | 27 ++++++-- packages/preset-tailwind/src/rules.test.ts | 7 +++ packages/preset-tailwind/src/types.ts | 10 +-- packages/preset-tailwind/src/variants.ts | 50 ++++++++++++++- 11 files changed, 216 insertions(+), 12 deletions(-) create mode 100644 .changeset/quiet-tips-begin.md diff --git a/.changeset/quiet-tips-begin.md b/.changeset/quiet-tips-begin.md new file mode 100644 index 000000000..5f95f1984 --- /dev/null +++ b/.changeset/quiet-tips-begin.md @@ -0,0 +1,5 @@ +--- +'@twind/preset-tailwind': minor +--- + +Add `aria-*` and `data-*` variants diff --git a/packages/intellisense/src/__snapshots__/enumerate.test.ts.snap b/packages/intellisense/src/__snapshots__/enumerate.test.ts.snap index 0c3ed3a32..b0dd43ef2 100644 --- a/packages/intellisense/src/__snapshots__/enumerate.test.ts.snap +++ b/packages/intellisense/src/__snapshots__/enumerate.test.ts.snap @@ -10,12 +10,25 @@ exports[`enumerate 1`] = ` "active:", "after:", "any-link:", + "aria-asc:", + "aria-checked:", + "aria-desc:", + "aria-disabled:", + "aria-expanded:", + "aria-hidden:", + "aria-pressed:", + "aria-readonly:", + "aria-required:", + "aria-selected:", + "aria-[:", "backdrop:", "before:", "checked:", "contrast-less:", "contrast-more:", "dark:", + "data-checked:", + "data-[:", "default:", "defined:", "disabled:", @@ -56,6 +69,17 @@ exports[`enumerate 1`] = ` "group-valid:", "group-visited:", "group-any-link:", + "group-aria-asc:", + "group-aria-checked:", + "group-aria-desc:", + "group-aria-disabled:", + "group-aria-expanded:", + "group-aria-hidden:", + "group-aria-pressed:", + "group-aria-readonly:", + "group-aria-required:", + "group-aria-selected:", + "group-data-checked:", "group-first-child:", "group-focus-visible:", "group-focus-within:", @@ -65,6 +89,8 @@ exports[`enumerate 1`] = ` "group-placeholder-shown:", "group-read-only:", "group-read-write:", + "group-aria-[:", + "group-data-[:", "group-first-of-type:", "group-last-of-type:", "group-only-of-type:", @@ -115,6 +141,17 @@ exports[`enumerate 1`] = ` "peer-valid:", "peer-visited:", "peer-any-link:", + "peer-aria-asc:", + "peer-aria-checked:", + "peer-aria-desc:", + "peer-aria-disabled:", + "peer-aria-expanded:", + "peer-aria-hidden:", + "peer-aria-pressed:", + "peer-aria-readonly:", + "peer-aria-required:", + "peer-aria-selected:", + "peer-data-checked:", "peer-first-child:", "peer-focus-visible:", "peer-focus-within:", @@ -124,6 +161,8 @@ exports[`enumerate 1`] = ` "peer-placeholder-shown:", "peer-read-only:", "peer-read-write:", + "peer-aria-[:", + "peer-data-[:", "peer-first-of-type:", "peer-last-of-type:", "peer-only-of-type:", diff --git a/packages/intellisense/src/__snapshots__/suggest-at.test.ts.snap b/packages/intellisense/src/__snapshots__/suggest-at.test.ts.snap index 0efce08be..84cf56bea 100644 --- a/packages/intellisense/src/__snapshots__/suggest-at.test.ts.snap +++ b/packages/intellisense/src/__snapshots__/suggest-at.test.ts.snap @@ -169,6 +169,7 @@ exports[`suggestAt html 1`] = ` "outline-red-800/", "outline-red-900/", "group-only-child:", + "group-aria-expanded:", "outline-emerald-50", "outline-emerald-100", "outline-emerald-200", @@ -360,6 +361,14 @@ exports[`suggestAt html 4`] = ` "rtl:", "group-active:", "group-any-link:", + "group-aria-checked:", + "group-aria-disabled:", + "group-aria-expanded:", + "group-aria-hidden:", + "group-aria-pressed:", + "group-aria-readonly:", + "group-aria-required:", + "group-aria-selected:", "group-checked:", "group-default:", "group-defined:", @@ -398,6 +407,14 @@ exports[`suggestAt html 4`] = ` "group-visited:", "peer-active:", "peer-any-link:", + "peer-aria-checked:", + "peer-aria-disabled:", + "peer-aria-expanded:", + "peer-aria-hidden:", + "peer-aria-pressed:", + "peer-aria-readonly:", + "peer-aria-required:", + "peer-aria-selected:", "peer-checked:", "peer-default:", "peer-defined:", @@ -434,6 +451,14 @@ exports[`suggestAt html 4`] = ` "peer-target:", "peer-valid:", "peer-visited:", + "aria-checked:", + "aria-disabled:", + "aria-expanded:", + "aria-hidden:", + "aria-pressed:", + "aria-readonly:", + "aria-required:", + "aria-selected:", "open:", "odd:", "even:", @@ -457,6 +482,12 @@ exports[`suggestAt html 4`] = ` "motion-safe:", "motion-reduce:", "sticky:", + "group-aria-[:", + "group-data-[:", + "peer-aria-[:", + "peer-data-[:", + "aria-[:", + "data-[:", ], } `; @@ -474,12 +505,22 @@ exports[`suggestAt html 5`] = ` "active:", "after:", "any-link:", + "aria-checked:", + "aria-disabled:", + "aria-expanded:", + "aria-hidden:", + "aria-pressed:", + "aria-readonly:", + "aria-required:", + "aria-selected:", + "aria-[:", "backdrop:", "before:", "checked:", "contrast-less:", "contrast-more:", "dark:", + "data-[:", "default:", "defined:", "disabled:", @@ -520,6 +561,14 @@ exports[`suggestAt html 5`] = ` "group-valid:", "group-visited:", "group-any-link:", + "group-aria-checked:", + "group-aria-disabled:", + "group-aria-expanded:", + "group-aria-hidden:", + "group-aria-pressed:", + "group-aria-readonly:", + "group-aria-required:", + "group-aria-selected:", "group-first-child:", "group-focus-visible:", "group-focus-within:", @@ -529,6 +578,8 @@ exports[`suggestAt html 5`] = ` "group-placeholder-shown:", "group-read-only:", "group-read-write:", + "group-aria-[:", + "group-data-[:", "group-first-of-type:", "group-last-of-type:", "group-only-of-type:", @@ -579,6 +630,14 @@ exports[`suggestAt html 5`] = ` "peer-valid:", "peer-visited:", "peer-any-link:", + "peer-aria-checked:", + "peer-aria-disabled:", + "peer-aria-expanded:", + "peer-aria-hidden:", + "peer-aria-pressed:", + "peer-aria-readonly:", + "peer-aria-required:", + "peer-aria-selected:", "peer-first-child:", "peer-focus-visible:", "peer-focus-within:", @@ -588,6 +647,8 @@ exports[`suggestAt html 5`] = ` "peer-placeholder-shown:", "peer-read-only:", "peer-read-write:", + "peer-aria-[:", + "peer-data-[:", "peer-first-of-type:", "peer-last-of-type:", "peer-only-of-type:", diff --git a/packages/intellisense/src/__snapshots__/suggest.test.ts.snap b/packages/intellisense/src/__snapshots__/suggest.test.ts.snap index 0749afc6a..a1ec3515c 100644 --- a/packages/intellisense/src/__snapshots__/suggest.test.ts.snap +++ b/packages/intellisense/src/__snapshots__/suggest.test.ts.snap @@ -112,6 +112,7 @@ exports[`suggest with single char input 1`] = ` "uppercase", "no-underline", "touch-pan-up", + "aria-required:", "default:", "focus-within:", "focus-visible:", @@ -141,6 +142,14 @@ exports[`suggest with single char input 1`] = ` "group-valid:", "group-visited:", "group-any-link:", + "group-aria-checked:", + "group-aria-disabled:", + "group-aria-expanded:", + "group-aria-hidden:", + "group-aria-pressed:", + "group-aria-readonly:", + "group-aria-required:", + "group-aria-selected:", "group-first-child:", "group-focus-visible:", "group-focus-within:", @@ -150,6 +159,8 @@ exports[`suggest with single char input 1`] = ` "group-placeholder-shown:", "group-read-only:", "group-read-write:", + "group-aria-[:", + "group-data-[:", "group-first-of-type:", "group-last-of-type:", "group-only-of-type:", @@ -163,6 +174,7 @@ exports[`suggest with single char input 1`] = ` "peer-fullscreen:", "peer-paused:", "peer-required:", + "peer-aria-required:", "peer-focus-visible:", "peer-focus-within:", "peer-out-of-range:", diff --git a/packages/intellisense/src/enumerate.test.ts b/packages/intellisense/src/enumerate.test.ts index 2bbe619f5..0d97f5cc8 100644 --- a/packages/intellisense/src/enumerate.test.ts +++ b/packages/intellisense/src/enumerate.test.ts @@ -7,6 +7,17 @@ import { createIntellisense } from '.' test('enumerate', () => { const intellisense = createIntellisense({ presets: [presetTailwind()], + theme: { + data: { + checked: 'ui~="checked"', + }, + extend: { + aria: { + asc: 'sort="ascending"', + desc: 'sort="descending"', + }, + }, + }, }) expect(Array.from(intellisense.enumerate(), ({ name }) => name)).toMatchSnapshot() diff --git a/packages/intellisense/src/suggest.test.ts b/packages/intellisense/src/suggest.test.ts index f57cc5c8f..5f3ce1509 100644 --- a/packages/intellisense/src/suggest.test.ts +++ b/packages/intellisense/src/suggest.test.ts @@ -16,7 +16,7 @@ const $ = (suggestions: Promise) => suggestions.then((suggestions) => suggestions.map(({ value }) => value)) test('suggest with empty input', async () => { - await expect(intellisense.suggest('')).resolves.toHaveLength(14680) + await expect(intellisense.suggest('')).resolves.toHaveLength(14710) }) test('suggest with single char input', async () => { diff --git a/packages/preset-tailwind/src/defaultTheme.ts b/packages/preset-tailwind/src/defaultTheme.ts index cd7d84519..c87af6c2f 100644 --- a/packages/preset-tailwind/src/defaultTheme.ts +++ b/packages/preset-tailwind/src/defaultTheme.ts @@ -8,9 +8,11 @@ import type { TailwindTheme } from './types' import * as colors from './colors' export type OmitedSections = + | 'aria' | 'backgroundPosition' | 'container' | 'cursor' + | 'data' | 'gridColumnEnd' | 'gridColumnStart' | 'gridRowEnd' @@ -19,7 +21,7 @@ export type OmitedSections = | 'objectPosition' | 'transformOrigin' -export type StableSections = + export type StableSections = | 'screens' | 'columns' | 'spacing' diff --git a/packages/preset-tailwind/src/rules.test.json b/packages/preset-tailwind/src/rules.test.json index 472edcfc6..f0f0420c9 100644 --- a/packages/preset-tailwind/src/rules.test.json +++ b/packages/preset-tailwind/src/rules.test.json @@ -1742,10 +1742,7 @@ ".backdrop-sepia{--tw-backdrop-sepia:sepia(100%);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}" ], "!underline": ".\\!underline{text-decoration-line:underline !important}", - "cursor bg-red-500": [ - "cursor bg-red-500", - [".bg-red-500{--tw-bg-opacity:1;background-color:rgba(239,68,68,var(--tw-bg-opacity))}"] - ], + "bg-red-500": ".bg-red-500{--tw-bg-opacity:1;background-color:rgba(239,68,68,var(--tw-bg-opacity))}", "~(text-(center 2xl) underline)": [ "~(text-center,text-2xl,underline)", [ @@ -2118,5 +2115,25 @@ "bg-[size:200px_100px]": ".bg-\\[size\\:200px_100px\\]{background-size:200px 100px}", "bg-[center_top_1rem]": ".bg-\\[center_top_1rem\\]{background-position:center top 1rem}", "bg-[position:center_top_1rem]": ".bg-\\[position\\:center_top_1rem\\]{background-position:center top 1rem}", - "before:absolute": ".before\\:absolute::before{content:var(--tw-content);position:absolute}" + "before:absolute": ".before\\:absolute::before{content:var(--tw-content);position:absolute}", + "aria-checked:underline": ".aria-checked\\:underline[aria-checked=\"true\"]{text-decoration-line:underline}", + "aria-[sort=ascending]:underline": ".aria-\\[sort\\=ascending\\]\\:underline[aria-sort=ascending]{text-decoration-line:underline}", + "group-aria-checked:underline": ".group[aria-checked=\"true\"] .group-aria-checked\\:underline{text-decoration-line:underline}", + "peer-aria-checked:underline": ".peer[aria-checked=\"true\"]~.peer-aria-checked\\:underline{text-decoration-line:underline}", + "group-aria-[sort=ascending]:underline": ".group[aria-sort=ascending] .group-aria-\\[sort\\=ascending\\]\\:underline{text-decoration-line:underline}", + "peer-aria-[sort=ascending]:underline": ".peer[aria-sort=ascending]~.peer-aria-\\[sort\\=ascending\\]\\:underline{text-decoration-line:underline}", + "aria-asc:underline": ".aria-asc\\:underline[aria-sort=\"ascending\"]{text-decoration-line:underline}", + "aria-desc:underline": ".aria-desc\\:underline[aria-sort=\"descending\"]{text-decoration-line:underline}", + "data-checked:underline": ".data-checked\\:underline[data-ui~=\"checked\"]{text-decoration-line:underline}", + "data-[position=top]:underline": ".data-\\[position\\=top\\]\\:underline[data-position=top]{text-decoration-line:underline}", + "group-data-checked:underline": ".group[data-ui~=\"checked\"] .group-data-checked\\:underline{text-decoration-line:underline}", + "peer-data-checked:underline": ".peer[data-ui~=\"checked\"]~.peer-data-checked\\:underline{text-decoration-line:underline}", + "group-data-[position=top]:underline": ".group[data-position=top] .group-data-\\[position\\=top\\]\\:underline{text-decoration-line:underline}", + "peer-data-[position=top]:underline": ".peer[data-position=top]~.peer-data-\\[position\\=top\\]\\:underline{text-decoration-line:underline}", + "aria-[labelledby=a_b]:underline": ".aria-\\[labelledby\\=a_b\\]\\:underline[aria-labelledby=a b]{text-decoration-line:underline}", + "group-aria-[labelledby=a_b]:underline": ".group[aria-labelledby=a b] .group-aria-\\[labelledby\\=a_b\\]\\:underline{text-decoration-line:underline}", + "peer-aria-[labelledby=a_b]:underline": ".peer[aria-labelledby=a b]~.peer-aria-\\[labelledby\\=a_b\\]\\:underline{text-decoration-line:underline}", + "data-[foo=bar_baz]:underline": ".data-\\[foo\\=bar_baz\\]\\:underline[data-foo=bar baz]{text-decoration-line:underline}", + "group-data-[foo=bar_baz]:underline": ".group[data-foo=bar baz] .group-data-\\[foo\\=bar_baz\\]\\:underline{text-decoration-line:underline}", + "peer-data-[foo=bar_baz]:underline": ".peer[data-foo=bar baz]~.peer-data-\\[foo\\=bar_baz\\]\\:underline{text-decoration-line:underline}" } diff --git a/packages/preset-tailwind/src/rules.test.ts b/packages/preset-tailwind/src/rules.test.ts index eb03b1dfd..fab938f71 100644 --- a/packages/preset-tailwind/src/rules.test.ts +++ b/packages/preset-tailwind/src/rules.test.ts @@ -33,6 +33,13 @@ const tw = twind( emerald: theme('colors.emerald.500 / theme(opacity.50)'), }, }), + aria: { + asc: 'sort="ascending"', + desc: 'sort="descending"', + }, + data: { + checked: 'ui~="checked"', + }, backgroundImage: { 'hero-pattern': "url('/img/hero-pattern.svg')", }, diff --git a/packages/preset-tailwind/src/types.ts b/packages/preset-tailwind/src/types.ts index e84184d18..a9f807ad3 100644 --- a/packages/preset-tailwind/src/types.ts +++ b/packages/preset-tailwind/src/types.ts @@ -81,7 +81,9 @@ export interface TailwindTheme extends BaseTheme { spacing: Record durations: Record> + accentColor: BaseTheme['colors'] animation: Record> + aria: Record aspectRatio: Record backdropBlur: Record backdropBrightness: Record @@ -106,12 +108,12 @@ export interface TailwindTheme extends BaseTheme { boxShadow: Record> boxShadowColor: BaseTheme['colors'] brightness: Record + caretColor: BaseTheme['colors'] container: Container + content: Record contrast: Record cursor: Record> - caretColor: BaseTheme['colors'] - accentColor: BaseTheme['colors'] - content: Record + data: Record divideColor: BaseTheme['colors'] divideOpacity: Record divideWidth: Record @@ -177,9 +179,9 @@ export interface TailwindTheme extends BaseTheme { textColor: BaseTheme['colors'] textDecorationColor: BaseTheme['colors'] textDecorationThickness: Record - textUnderlineOffset: Record textIndent: Record textOpacity: Record + textUnderlineOffset: Record transformOrigin: Record transitionDelay: Record> transitionDuration: Record> diff --git a/packages/preset-tailwind/src/variants.ts b/packages/preset-tailwind/src/variants.ts index a1076a9e8..6fb0174e0 100644 --- a/packages/preset-tailwind/src/variants.ts +++ b/packages/preset-tailwind/src/variants.ts @@ -2,7 +2,13 @@ * @module @twind/preset-tailwind/variants */ -import type { AutocompleteProvider, VariantResolver, Variant, AutocompleteItem } from '@twind/core' +import { + AutocompleteProvider, + VariantResolver, + Variant, + AutocompleteItem, + arbitrary, +} from '@twind/core' import type { TailwindTheme } from './types' import { DEV } from 'distilt/env' @@ -41,6 +47,48 @@ const variants: Variant[] = [ // All other pseudo classes are already supported by twind + [ + '(aria|data)-', + withAutocomplete$( + ({ 1: $1 /* aria or data */, $$ /* everything after the dash */ }, context) => + `&[${$1}-${ + // aria-asc or data-checked -> from theme + context.theme($1, $$) || + // aria-[...] or data-[...] + arbitrary($$, $1, context) || + // default handling + `${$$}="true"` + }]`, + DEV && + (({ 1: $1 }, { theme }) => + [ + ...new Set([ + ...($1 == 'aria' + ? [ + 'checked', + 'disabled', + 'expanded', + 'hidden', + 'pressed', + 'readonly', + 'required', + 'selected', + ] + : []), + ...Object.keys(theme($1 as 'aria' | 'data') || {}), + ]), + ] + .map( + (key): AutocompleteItem => ({ + suffix: key, + label: `&[${$1}-${theme($1, key) || `${key}="true"`}]`, + theme: { section: $1, key }, + }), + ) + .concat([{ suffix: '[', label: `&[${$1}-…]` }])), + ), + ], + /* Styling based on parent and peer state */ // Groups classes like: group-focus and group-hover // these need to add a marker selector with the pseudo class