When using hook_css_alter, sometimes extra aggregates are produced. This is because drupal_group_css() expects the items to be sorted such that matching groups are adjacent in the list. However, drupal_sort_css_js ignores 'media' and 'browser' keys, so for example if a print stylesheet appears in between two media=all stylesheets then you get extra aggregates.

Steps to reproduce:
1) Drop this alter hook somewhere:

function mymodule_css_alter(&$css) {
  foreach ($css as $name => $style) {
    $css[$name]['group'] = CSS_DEFAULT;
    $css[$name]['every_page'] = FALSE;
  }
}

2) Enable aggregation

Expected behavior: only one media=all stylesheet will be output, since the alter hook has forced them into the same group.
Actual behavior: 3 media=all stylesheets are output (not counting conditional IE styles)

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

effulgentsia’s picture

I don't think we want this. Ideally, we don't want to mess with CSS rule order. If a media="print" stylesheet is weighted between 2 media="all" stylesheets, then I think it's better to allow 3 aggregate files to be produced than to disrespect the weights. In #769226: Optimize JS/CSS aggregation for front-end performance and DX, we introduced a 'every_page' key that has greater priority than weight, but this was done because there was a compelling performance reason to do it, and it appeared to make some sense. However, even that has proven to confuse people: #977844: Remove the 'every_page' option for CSS/JS assets: it is confusing, even damaging.

effulgentsia’s picture

Issue tags: +markup

Tagging so that people watching the markup queue see this, since any proposed changes to CSS rule order affects them.

grendzy’s picture

Hm, I see what you mean about respecting weights. I dug deeper to see why core would assign such strange weights (such that print / IE styles would be sandwiched in between global styles), and it's because of this in drupal_add_css:

// Always add a tiny value to the weight, to conserve the insertion order.
$options['weight'] += count($css) / 1000;

Here are the groups that are produced by the default install (after applying the above css_alter). You can see that Bartik's print and IE stylesheets break the global styles into 3 groups, and the weights are essentially arbitrary:

----- new group -----
themes/bartik/css/layout.css,       weight: 0,     media: all
themes/bartik/css/style.css,        weight: 0.001, media: all
themes/bartik/css/colors.css,       weight: 0.002, media: all
----- new group -----
themes/bartik/css/print.css,        weight: 0.003, media: print
----- new group -----
misc/ui/jquery.ui.core.css,         weight: 0.004, media: all
misc/ui/jquery.ui.theme.css,        weight: 0.005, media: all
modules/overlay/overlay-parent.css, weight: 0.006, media: all
modules/system/system.base.css,     weight: 0.007, media: all
modules/system/system.menus.css,    weight: 0.008, media: all
modules/system/system.messages.css, weight: 0.009, media: all
modules/system/system.theme.css,    weight: 0.01,  media: all
modules/comment/comment.css,        weight: 0.011, media: all
modules/field/theme/field.css,      weight: 0.012, media: all
modules/node/node.css,              weight: 0.013, media: all
modules/search/search.css,          weight: 0.014, media: all
modules/user/user.css,              weight: 0.015, media: all
modules/contextual/contextual.css,  weight: 0.016, media: all
----- new group -----
themes/bartik/css/ie.css,           weight: 0.017, media: all
----- new group -----
themes/bartik/css/ie6.css,          weight: 0.018, media: all
----- new group -----
modules/shortcut/shortcut.css,      weight: 0.019, media: all
modules/toolbar/toolbar.css,        weight: 0.02,  media: all

Preserving insertion order is quite handy, but I'm having a hard time imagining a developer wanting insertion order to have a higher precedence than media type or browser. What do you think about sorting by floor(weight), then browser/media, then weight again? That way explicit integer weights could still "break out" of a group, but implicit fractional weights could not.

grendzy’s picture

FileSize
2.31 KB

Here's an updated patch. The main change is sorting by integer weight first, which prevents implicit weights from splitting a group, but still allows explicit weights to break out of a group. Insertion order is still preserved within groups.

I also made the sort callback more compact by just using subtraction to compare numeric values - rather than separate conditions for less than / greater than.

alanburke’s picture

subscribe

tomgf’s picture

subscribe