@@ -23,9 +23,15 @@ export class CSSGenerator {
2323
2424 // Try built-in rules
2525 for ( const rule of builtInRules ) {
26- const properties = rule ( parsed , this . config )
27- if ( properties ) {
28- this . addRule ( parsed , properties )
26+ const result = rule ( parsed , this . config )
27+ if ( result ) {
28+ // Handle both old format (just properties) and new format (object with properties and childSelector)
29+ if ( 'properties' in result && typeof result . properties === 'object' ) {
30+ this . addRule ( parsed , result . properties , result . childSelector )
31+ }
32+ else {
33+ this . addRule ( parsed , result as Record < string , string > )
34+ }
2935 return
3036 }
3137 }
@@ -55,8 +61,14 @@ export class CSSGenerator {
5561 /**
5662 * Add a CSS rule with variants applied
5763 */
58- private addRule ( parsed : ParsedClass , properties : Record < string , string > ) : void {
59- const selector = this . buildSelector ( parsed )
64+ private addRule ( parsed : ParsedClass , properties : Record < string , string > , childSelector ?: string ) : void {
65+ let selector = this . buildSelector ( parsed )
66+
67+ // Append child selector if provided
68+ if ( childSelector ) {
69+ selector += ` ${ childSelector } `
70+ }
71+
6072 const mediaQuery = this . getMediaQuery ( parsed )
6173
6274 // Apply !important modifier
@@ -75,17 +87,20 @@ export class CSSGenerator {
7587 selector,
7688 properties,
7789 mediaQuery,
90+ childSelector,
7891 } )
7992 }
8093
8194 /**
82- * Build CSS selector with pseudo-classes
95+ * Build CSS selector with pseudo-classes and variants
8396 */
8497 private buildSelector ( parsed : ParsedClass ) : string {
8598 let selector = `.${ this . escapeSelector ( parsed . raw ) } `
99+ let prefix = ''
86100
87- // Apply pseudo-class variants (non-responsive)
101+ // Apply variants
88102 for ( const variant of parsed . variants ) {
103+ // Pseudo-class variants
89104 if ( variant === 'hover' && this . config . variants . hover ) {
90105 selector += ':hover'
91106 }
@@ -98,27 +113,101 @@ export class CSSGenerator {
98113 else if ( variant === 'disabled' && this . config . variants . disabled ) {
99114 selector += ':disabled'
100115 }
116+ else if ( variant === 'visited' && this . config . variants . visited ) {
117+ selector += ':visited'
118+ }
119+ else if ( variant === 'checked' && this . config . variants . checked ) {
120+ selector += ':checked'
121+ }
122+ else if ( variant === 'focus-within' && this . config . variants [ 'focus-within' ] ) {
123+ selector += ':focus-within'
124+ }
125+ else if ( variant === 'focus-visible' && this . config . variants [ 'focus-visible' ] ) {
126+ selector += ':focus-visible'
127+ }
128+ // Positional variants
129+ else if ( variant === 'first' && this . config . variants . first ) {
130+ selector += ':first-child'
131+ }
132+ else if ( variant === 'last' && this . config . variants . last ) {
133+ selector += ':last-child'
134+ }
135+ else if ( variant === 'odd' && this . config . variants . odd ) {
136+ selector += ':nth-child(odd)'
137+ }
138+ else if ( variant === 'even' && this . config . variants . even ) {
139+ selector += ':nth-child(even)'
140+ }
141+ else if ( variant === 'first-of-type' && this . config . variants [ 'first-of-type' ] ) {
142+ selector += ':first-of-type'
143+ }
144+ else if ( variant === 'last-of-type' && this . config . variants [ 'last-of-type' ] ) {
145+ selector += ':last-of-type'
146+ }
147+ // Pseudo-elements
148+ else if ( variant === 'before' && this . config . variants . before ) {
149+ selector += '::before'
150+ }
151+ else if ( variant === 'after' && this . config . variants . after ) {
152+ selector += '::after'
153+ }
154+ // Group/Peer variants
155+ else if ( variant . startsWith ( 'group-' ) && this . config . variants . group ) {
156+ const groupVariant = variant . slice ( 6 ) // Remove 'group-'
157+ prefix = `.group:${ groupVariant } `
158+ }
159+ else if ( variant . startsWith ( 'peer-' ) && this . config . variants . peer ) {
160+ const peerVariant = variant . slice ( 5 ) // Remove 'peer-'
161+ prefix = `.peer:${ peerVariant } ~ `
162+ }
163+ // Dark mode
101164 else if ( variant === 'dark' && this . config . variants . dark ) {
102- // Dark mode variant prepends a selector
103- selector = `.dark ${ selector } `
165+ prefix = '.dark '
166+ }
167+ // Direction variants
168+ else if ( variant === 'rtl' && this . config . variants . rtl ) {
169+ prefix = '[dir="rtl"] '
170+ }
171+ else if ( variant === 'ltr' && this . config . variants . ltr ) {
172+ prefix = '[dir="ltr"] '
104173 }
105174 }
106175
107- return selector
176+ return prefix + selector
108177 }
109178
110179 /**
111- * Get media query for responsive variants
180+ * Get media query for responsive and media variants
112181 */
113182 private getMediaQuery ( parsed : ParsedClass ) : string | undefined {
114- if ( ! this . config . variants . responsive ) {
115- return undefined
116- }
117-
118183 for ( const variant of parsed . variants ) {
119- const breakpoint = this . config . theme . screens [ variant ]
120- if ( breakpoint ) {
121- return `@media (min-width: ${ breakpoint } )`
184+ // Responsive breakpoints
185+ if ( this . config . variants . responsive ) {
186+ const breakpoint = this . config . theme . screens [ variant ]
187+ if ( breakpoint ) {
188+ return `@media (min-width: ${ breakpoint } )`
189+ }
190+ }
191+
192+ // Print media
193+ if ( variant === 'print' && this . config . variants . print ) {
194+ return '@media print'
195+ }
196+
197+ // Motion preferences
198+ if ( variant === 'motion-safe' && this . config . variants [ 'motion-safe' ] ) {
199+ return '@media (prefers-reduced-motion: no-preference)'
200+ }
201+ if ( variant === 'motion-reduce' && this . config . variants [ 'motion-reduce' ] ) {
202+ return '@media (prefers-reduced-motion: reduce)'
203+ }
204+
205+ // Contrast preferences
206+ if ( variant === 'contrast-more' && this . config . variants [ 'contrast-more' ] ) {
207+ return '@media (prefers-contrast: more)'
208+ }
209+ if ( variant === 'contrast-less' && this . config . variants [ 'contrast-less' ] ) {
210+ return '@media (prefers-contrast: less)'
122211 }
123212 }
124213
@@ -138,6 +227,12 @@ export class CSSGenerator {
138227 toCSS ( minify = false ) : string {
139228 const parts : string [ ] = [ ]
140229
230+ // Add preflight CSS first
231+ for ( const preflight of this . config . preflights ) {
232+ const preflightCSS = preflight . getCSS ( )
233+ parts . push ( minify ? preflightCSS . replace ( / \s + / g, ' ' ) . trim ( ) : preflightCSS )
234+ }
235+
141236 // Base rules (no media query)
142237 const baseRules = this . rules . get ( 'base' ) || [ ]
143238 if ( baseRules . length > 0 ) {
0 commit comments