@@ -36,6 +36,9 @@ pub struct StylesheetOptions {
3636 pub include_static : bool ,
3737 pub source_map : bool ,
3838 pub emit_layer_declaration : bool ,
39+ /// When set, `split_css` emits only the selected layers (and rebuilds the
40+ /// index `@layer` preamble + `@import` lines to match).
41+ pub layers : Option < Vec < StylesheetLayer > > ,
3942}
4043
4144impl Default for StylesheetOptions {
@@ -45,6 +48,7 @@ impl Default for StylesheetOptions {
4548 include_static : false ,
4649 source_map : false ,
4750 emit_layer_declaration : true ,
51+ layers : None ,
4852 }
4953 }
5054}
@@ -296,6 +300,10 @@ pub fn split_css(input: &StylesheetInput<'_>, options: &StylesheetOptions) -> Ve
296300 true ,
297301 ) ;
298302
303+ let selected = options. layers . as_deref ( ) ;
304+ let layer_selected =
305+ |layer : StylesheetLayer | selected. is_none_or ( |layers| layers. contains ( & layer) ) ;
306+
299307 let mut files: Vec < SplitCssFile > = Vec :: new ( ) ;
300308 let mut imports: Vec < String > = Vec :: new ( ) ;
301309
@@ -305,6 +313,9 @@ pub fn split_css(input: &StylesheetInput<'_>, options: &StylesheetOptions) -> Ve
305313 ( StylesheetLayer :: Tokens , "tokens.css" ) ,
306314 ( StylesheetLayer :: Utilities , "utilities.css" ) ,
307315 ] {
316+ if !layer_selected ( layer) {
317+ continue ;
318+ }
308319 let css = full
309320 . layer_ranges
310321 . get ( layer)
@@ -319,38 +330,51 @@ pub fn split_css(input: &StylesheetInput<'_>, options: &StylesheetOptions) -> Ve
319330 }
320331 }
321332
322- let recipe_files = emitter:: emit_recipe_split ( input. config , & utility, recipes, options. minify ) ;
323- if !recipe_files. is_empty ( ) {
324- let mut recipe_imports = Vec :: with_capacity ( recipe_files. len ( ) ) ;
325- for ( name, css) in & recipe_files {
326- recipe_imports. push ( format ! ( "@import './recipes/{name}.css';" ) ) ;
333+ if layer_selected ( StylesheetLayer :: Recipes ) {
334+ let recipe_files =
335+ emitter:: emit_recipe_split ( input. config , & utility, recipes, options. minify ) ;
336+ if !recipe_files. is_empty ( ) {
337+ let mut recipe_imports = Vec :: with_capacity ( recipe_files. len ( ) ) ;
338+ for ( name, css) in & recipe_files {
339+ recipe_imports. push ( format ! ( "@import './recipes/{name}.css';" ) ) ;
340+ files. push ( SplitCssFile {
341+ path : format ! ( "styles/recipes/{name}.css" ) ,
342+ code : ensure_trailing_newline ( css) ,
343+ } ) ;
344+ }
327345 files. push ( SplitCssFile {
328- path : format ! ( "styles/recipes/{name} .css" ) ,
329- code : ensure_trailing_newline ( css ) ,
346+ path : "styles/recipes.css" . to_owned ( ) ,
347+ code : format ! ( "{} \n " , recipe_imports . join ( " \n " ) ) ,
330348 } ) ;
349+ imports. push ( "@import './styles/recipes.css';" . to_owned ( ) ) ;
331350 }
332- files. push ( SplitCssFile {
333- path : "styles/recipes.css" . to_owned ( ) ,
334- code : format ! ( "{}\n " , recipe_imports. join( "\n " ) ) ,
335- } ) ;
336- imports. push ( "@import './styles/recipes.css';" . to_owned ( ) ) ;
337351 }
338352
339- for ( theme_name, css) in
340- theme_css_entries_from_dictionary ( input. config , token_dictionary. as_deref ( ) , options. minify )
341- {
342- if css. trim ( ) . is_empty ( ) {
343- continue ;
353+ if layer_selected ( StylesheetLayer :: Tokens ) {
354+ for ( theme_name, css) in theme_css_entries_from_dictionary (
355+ input. config ,
356+ token_dictionary. as_deref ( ) ,
357+ options. minify ,
358+ ) {
359+ if css. trim ( ) . is_empty ( ) {
360+ continue ;
361+ }
362+ files. push ( SplitCssFile {
363+ path : format ! ( "styles/themes/{}.css" , file_stem( & theme_name) ) ,
364+ code : ensure_trailing_newline ( & css) ,
365+ } ) ;
344366 }
345- files. push ( SplitCssFile {
346- path : format ! ( "styles/themes/{}.css" , file_stem( & theme_name) ) ,
347- code : ensure_trailing_newline ( & css) ,
348- } ) ;
349367 }
350368
351369 // `styles.css` entry: the @layer order declaration + the imports above.
352- let mut index = layer_order_line ( & input. config . layers ) ;
353- index. push ( '\n' ) ;
370+ let mut index = if options. emit_layer_declaration {
371+ layer_order_declaration ( & input. config . layers , selected)
372+ } else {
373+ String :: new ( )
374+ } ;
375+ if !index. is_empty ( ) {
376+ index. push ( '\n' ) ;
377+ }
354378 if !imports. is_empty ( ) {
355379 index. push_str ( & imports. join ( "\n " ) ) ;
356380 index. push ( '\n' ) ;
@@ -441,13 +465,37 @@ fn ensure_trailing_newline(css: &str) -> String {
441465 }
442466}
443467
444- fn layer_order_line ( layers : & pandacss_config:: CascadeLayers ) -> String {
445- let names = layers. declaration_names ( ) ;
468+ /// Return the cascade layer order declaration for all configured layers or a
469+ /// selected subset.
470+ #[ must_use]
471+ pub fn layer_order_declaration (
472+ layers : & pandacss_config:: CascadeLayers ,
473+ selected : Option < & [ StylesheetLayer ] > ,
474+ ) -> String {
475+ let names: Vec < String > = layers
476+ . ordered ( )
477+ . iter ( )
478+ . filter_map ( |( semantic, name) | {
479+ let layer = StylesheetLayer :: from_name ( semantic) ?;
480+ selected
481+ . is_none_or ( |selected_layers| selected_layers. contains ( & layer) )
482+ . then ( || ( * name) . to_owned ( ) )
483+ } )
484+ . collect ( ) ;
485+ if names. is_empty ( ) {
486+ return String :: new ( ) ;
487+ }
488+ if names. len ( ) <= 3 {
489+ return format ! ( "@layer {};" , names. join( ", " ) ) ;
490+ }
491+ if names. len ( ) == 4 {
492+ return format ! ( "@layer {},\n {};" , names[ ..3 ] . join( ", " ) , names[ 3 ] ) ;
493+ }
446494 format ! (
447495 "@layer {},\n {},\n {};" ,
448496 names[ ..3 ] . join( ", " ) ,
449497 names[ 3 ..names. len( ) - 1 ] . join( ", " ) ,
450- names. last ( ) . expect ( "layer declaration names" )
498+ names[ names . len ( ) - 1 ]
451499 )
452500}
453501
0 commit comments