1
votes

I was planning to use LESS css in my project (PHP). I am planning to use its nested @media query feature. I find that it fails to group the multiple media queries in the output css it generates.

For example:

// LESS
.header {
  @media all and (min-width: 240px) and (max-width: 319px) {
    font-size: 12px;
  }

  @media all and (min-width: 320px) and (max-width: 479px) {
    font-size: 16px;
    font-weight: bold;
  }
}

.body {
  @media all and (min-width: 240px) and (max-width: 319px) {
    font-size: 10px;
  }

  @media all and (min-width: 320px) and (max-width: 479px) {
    font-size: 12px;
  }
}

// output CSS
@media all and (min-width: 240px) and (max-width: 319px) {
  .header {
    font-size: 12px;
  }
}
@media all and (min-width: 320px) and (max-width: 479px) {
  .header {
    font-size: 16px;
    font-weight: bold;
  }
}
@media all and (min-width: 240px) and (max-width: 319px) {
  .body {
    font-size: 10px;
  }
}
@media all and (min-width: 320px) and (max-width: 479px) {
  .body {
    font-size: 12px;
  }
}

My expected output is (@media queries grouped)

@media all and (min-width: 240px) and (max-width: 319px) {
  .header {
    font-size: 12px;
  }
  .body {
    font-size: 10px;
  }
}
@media all and (min-width: 320px) and (max-width: 479px) {
  .header {
    font-size: 16px;
    font-weight: bold;
  }
  .body {
    font-size: 12px;
  }
}

I would like to know if it can be done in LESS (PHP) it self or is there any simple PHP based CSS parser I can use to manipulate the output CSS to group and merge the @media queries. as shown in the below flow.

  LESS file
      |
      V
[LESSphp compiler]
      |
      V
   CSS file
      |
      V
The CSS file generated
would undergo my script
written using CSS parser
      |
      V
   CSS file
with similar @media
grouped.

In case achieving grouped @media queries in LESS (PHP) is not an option I would like to know your suggestions on CSS parser (PHP) that can be used in the above flow.

2
It can't do this - it can't guarentee one media query will be matched and another one won't and whether one selector will override another one.Luke Page
If it is not possible to do it in LESS itself then I would like to know if there are any simple CSS parsers in PHP using which I can manipulate the output file so that I will programmatically achieve this.Goje87

2 Answers

4
votes

I adapt autoCompileLess function to group media query at the end of CSS without changing the less code.

@mobile: ~"only screen and (max-width: 529px)";
.test {
    color:green;
    @media @mobile { color:red; }
}    
.test2 {
    color:red;
    @media @mobile { color:blue; }
}

Compile with less by default

.test {
    color:green;
}    
.test2 {
    color:red;
}
@media only screen and (max-width: 529px) {
    .test {
        color:red;
    }
}    
@media only screen and (max-width: 529px) {
    .test2 {
        color:blue;
    }
}

Compile less with the following function

.test {
    color:green;
}    
.test2 {
    color:red;
}
@media only screen and (max-width: 529px) {
    .test {
        color:red;
    }
    .test2 {
        color:blue;
    }
}

And the function :

<?php
function autoCompileLess($inputFile, $outputFile)
{
    // load cache
    $cacheFile = $inputFile.".cache";

    if (file_exists($cacheFile))
    {
        $cache = unserialize(file_get_contents($cacheFile));
        if (empty($cache)) $cache = $inputFile;
    }
    else
    {
        $cache = $inputFile;
    }

    // compile less
    $less = new lessc;
    $newCache = $less->cachedCompile($cache);

    // save less cache
    $saveCache = $newCache;

    // search media query in CSS
    preg_match_all('#@media(.*?)\{(.+?}[ \n])\}#si',$newCache['compiled'],$match,PREG_SET_ORDER);//$MatchingString now hold all strings matching $pattern.


    $media = array();

    // group same media query
    foreach ($match as $val)
    {
        if (!isset($media[$val[1]])) $media[$val[1]] = '';

        $media[$val[1]] .= $val[2];
    }

    // delete media query of cache
    $newCache['compiled'] = preg_replace('#@media(.*?)\{(.+?}[ \n])\}#si', '', $newCache['compiled']);

    // add groups of media query at the end of CSS
    $final = $newCache['compiled'] . "\n";
    foreach ($media as $id => $val)
    {
        $final .= "\n" . '@media' . $id . '{' . $val . '}' . "\n";
    }

    // save less
    // save CSS with groups of media query
    if (!is_array($cache) || $newCache["updated"] > $cache["updated"]) 
    {
        file_put_contents($cacheFile, serialize($saveCache));
        // file_put_contents($outputFile, $newCache['compiled']);
        file_put_contents($outputFile, $final);
    }
}

// use of function
autoCompileLess('style.less', 'style.css');
0
votes

Why don't you have your selectors in your media queries too, like your expected output? Then you would get rid of having double media queries for everything you do...