Latest News: (loading..)
beerbee

Combining all css into one

25 posts in this topic

Hi,

https://gtmetrix.com/

advises to combine all .css files into one. Then I found that: https://manas.tungare.name/software/css-compression-in-php/ leading me there

https://gist.github.com/manastungare/2625128 

and then this came out

<?php $cssFiles = array(
  "ext/bootstrap/css/bootstrap.min.css",
  "custom.css",
  "user.css"
);
/**
 * Ideally, you wouldn't need to change any code beyond this point.
 */
$buffer = "";
foreach ($cssFiles as $cssFile) {
  $buffer .= file_get_contents($cssFile);
}
// Remove comments
$buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);
// Remove space after colons
//$buffer = str_replace(': ', ':', $buffer);
// Remove whitespace
//$buffer = str_replace(array("\r\n", "\r", "\n", "\t"), '', $buffer);
// Collapse adjacent spaces into a single space
//$buffer = ereg_replace(" {2,}", ' ',$buffer);
$buffer = preg_replace('/[\t\r\n]+/', '', $buffer);
$buffer = preg_replace('/[\s]{2,}/', ' ', $buffer);
$buffer = str_replace(': ', ':', $buffer);
// Remove spaces that might still be left where we know they aren't needed
$buffer = str_replace(array('} '), '}', $buffer);
$buffer = str_replace(array('{ '), '{', $buffer);
$buffer = str_replace(array('; '), ';', $buffer);
$buffer = str_replace(array(', '), ',', $buffer);
$buffer = str_replace(array(' }'), '}', $buffer);
$buffer = str_replace(array(' {'), '{', $buffer);
$buffer = str_replace(array(' ;'), ';', $buffer);
$buffer = str_replace(array(' ,'), ',', $buffer);
// Enable GZip encoding.
ob_start("ob_gzhandler");
// Enable caching
header('Cache-Control: public');
// Expire in one day
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 86400) . ' GMT');
// Set the correct MIME type, because Apache won't set it for us
header("Content-type: text/css");
// Write everything out
echo($buffer);
?>

Saved as css_compress.php loaded up to catalog root and included into template_top.php  with 

<link rel="stylesheet" type="text/css" media="screen, print, projection" href="css_compress.php"  />

instead of the single css files, only problem is now that glyphicon fonts (which i will get rid of anyway) gives 404.

Another question would be how to get css files of addons into that array also.

Any thoughts ideas?

Best regards

Christoph

Share this post


Link to post
Share on other sites

While combining (and minifying) CSS files can reduce load times, you have to be careful about a couple things:

  1. The order in which CSS entries will be seen (take priority) shouldn't change.
  2. Relative addresses (such as your glyphicon fonts) might change.

It also increases the burden on you when one of the component files changes, and you have to re-create the new combined file (especially if you have to tweak paths and update a new CSS file).

Edited by MrPhil

Share this post


Link to post
Share on other sites

Hi @MrPhil,

the css is rendered in the order of the files in the array, which means in this case user.css at last, just the normal behaviour.

Relative addresses a problem - yes true.

The file is rendered on the fly with adjustable caching, so there is no need to recreate manually after changing one of the css files.

Best regards

Christoph

Edited by beerbee

Share this post


Link to post
Share on other sites

Does combining the .css help to speed up the page load?

If yes, by what factor?  Or put another way;

Does the Return [better faster load] On Investment [time and effort of coding] make sense?

Share this post


Link to post
Share on other sites

I can't tell exactly but it helps definitely https://testmysite.withgoogle.com/ I get 5 seconds on the index that was worse before. 

PageSpeed Score (95%) YSlow Score (81%) for desktop at gtmetrix.com. I think every little bit helps.

The coding effort, I'd really like to know if it was possible to get something like $oscTemplate->addBlock to add css to the array instead injecting into the page..

The simple wrong way is not so much of an effort -> comment out in module, add to array in css_compress.php.

Another thing I like about it: No more unminify for editing and then minifying again, I added bootstrap.css to the array instead of min as it makes no difference anymore.

Best regards

Christoph

Share this post


Link to post
Share on other sites

A very interesting read in the context IMHO: http://carlofontanos.com/get-a-perfect-score-of-100-on-google-pagespeed-insights/

Especially the part about "Get rid of render blocking scripts" might lead me to more attempts to load all js and css stuff in on place in the right order but no more before </head> except https://gist.github.com/carlo-fontanos/abc69dfea9d1e853c0e49fe509dbaa4b for template_top.php

The usage part in before mentioned  link part would go  into template botttom.

I will according to my limited abilities do simple evil core and addon code changes just for the proof of concept, and be happy to get help implementing this in a more like $oscTemplate->addBlock or $oscTemplate->getBlocks('footer_scripts') way. This could be a general way of injecting/adding javascript and css in a very elegant and speedy way in the future. 

Best regards

Christoph

Edited by beerbee

Share this post


Link to post
Share on other sites

On the other hand, loading bootstrap from a cdn may save your first-time visitors loading most of the css if they already have it cached, and offer them a quicker load than you do anyway, depending on their location and that of your server.

I hope you have already addressed image sizes and are running a high php version, your database is well-maintained and your sessions are getting cleared down. If you are running lots of addons it may be the case that you're running too many separate queries to build your page and it may be that you need it optimising in some kind of super-addon that combines them and shares data - but I'm guessing. Any of these would make a much bigger difference than combining the css. BS is already minified and custom.css is as small as possible without losing legibility. You are saving a couple of server requests but not much data.

Share this post


Link to post
Share on other sites

Hi John,

nevertheless requests are time consuming too, ideally you would have one css an one js file to load non blocking - and it really depends how much blown up is the user css and how many and big additional css scripts are loaded by addons. 

At the moment I'm making experiments with the way css (in combination with initial post) and js is loaded mentioned in the post just above yours and it is really interesting - only blocking script is cookie.js ATM to be complained about by speed testing tools .

As for images: Im trying my best kraken.io  for compression, KissIT for thumbs, and the <picture> approach for logo and slides.

Which means: I want to look my logo not looking pixelated on hires devices but have smaller versions loaded on smaller devices.

Module for logo in header looks like this (hardcoded just for proof of concept):

<div id="storeLogo" class="col-sm-<?php echo $content_width; ?> storeLogo">
  <?php echo '<a href="' . tep_href_link('index.php') . '">  <picture>
  <source media="(min-width: 1000px)" srcset="images/store_logo.png">
  <source media="(min-width: 600px)" srcset="images/store_logo_900.png">
  <source media="(min-width: 400px)" srcset="images/store_logo_600.png">
   <source media="(min-width: 300px)" srcset="images/store_logo_400.png">' . tep_image('images/' . STORE_LOGO, STORE_NAME) . '</picture></a>'; ?>
</div>

Best regards

Christoph

Edited by beerbee

Share this post


Link to post
Share on other sites

What I would like to do in the future, and this is something @wHiTeHaT and I discussed years ago at the start of the BS build is responsive images, but at the time I am pretty sure that neither of us had the time to spend on finding/coding a good image resizer. 

I would prefer to use srcset ( https://css-tricks.com/responsive-images-youre-just-changing-resolutions-use-srcset/ ), .  My general idea is for each image uploaded, creates 4 or 5 image sizes of it;

1 for XS
1 for SM
1 for MD
1 for LG
1 for XL

We then amend tep_image to output a srcset, and then relax in the knowledge that the sites images are awesome.  That would be my next step for optimisation...as I believe that would bring the most benefit to the most number of shopowners...

Share this post


Link to post
Share on other sites
11 minutes ago, beerbee said:

@burt yes. I asked @raiwa I think KissIT could do it.

 

I'll have a look and give it a try these days

beerbee likes this

Share this post


Link to post
Share on other sites

@Burt i like the V3 approach better to be honest.

I explain...

First there is option to create "Settings" for images

Group: Default
thumb  = 20x20px

small  100x100px

medium 200x200px

large 500x500px

original ?????X????px

Group: products_listings
thumb  = 20x20px

small  100x100px

medium 200x200px

large 500x500px

 

Group: Shopping_cart

thumb  = 10x10px

small  50x50px

medium 150x150px

large 300x300px

extra_large 500x500px

Group: Boxes (or even box name)

thumb  = 10x10px

small  50x50px

medium 150x150px

large 300x300px

extra_large 500x500px

Each Group, image define and image sizes can be configured inside a image settings page.

When upload an image.... each size per group is generated.

Good for new modules/addons require images like a Blog or product compare...
Just tell via the new module to create a new image group

Group Blog:

thumb xxxx

small xxxxx

etc

 

 

As al goes automated, no worrys.
Look at:
https://github.com/osCommerce/oscommerce/blob/master/osCommerce/OM/Core/Site/Admin/Application/products/classes/rpc.php#L49
until the bottom of the file.

 

My 5cents (do it right!)

(i explained a little wrong)

Just a list of ALL sizes and namings you want........... later can assign these sizes to a specified group, but i think you get the "picture"

Assoon as you make a new size, all images will be builded from the original sources

Edited by wHiTeHaT
burt likes this

Share this post


Link to post
Share on other sites

I like the v3 system for making images.  The script in the background/admin is decent.

The usage of the created images is not ideal.

Ideal:
<img srcset> the image output

Next best:
<picture>

Remember that v3 is built prior to the days of worrying about responsive and bandwidth.
So we could hybrid the two ideas?

 

 

Share this post


Link to post
Share on other sites
1 hour ago, beerbee said:

I think KissIT could do it.

I don't know.  I've not ever taken the time to delve deep into this addon...

Share this post


Link to post
Share on other sites

There my class to do that, work perfect (for me :) ).

<?php
/**
* 
* @license GNU Public License V2.0
* @version $Id:
*/

  namespace ClicShopping\Apps\Catalog\Products\Classes\ClicShoppingAdmin;

  class ImageResample {

    protected $image;
    protected $image_type;
    protected $filename;
    protected $height;
    protected $width;
    protected $size;
    protected $scale;
    protected $x;
    protected $y;

    public function __construct($filename = null){
      if(!empty($filename)){
        $this->load($filename);
      }
    }

    public function load($filename) {
      $image_info = getimagesize($filename);
      $this->image_type = $image_info[2];
      if( $this->image_type == IMAGETYPE_JPEG ) {
        $this->image = imagecreatefromjpeg($filename);
      } elseif( $this->image_type == IMAGETYPE_GIF ) {
        $this->image = imagecreatefromgif($filename);
      } elseif( $this->image_type == IMAGETYPE_PNG ) {
        $this->image = imagecreatefrompng($filename);
      } else {
        throw new Exception("The file you're trying to open is not supported");
      }

    }

    public function save($filename, $image_type=IMAGETYPE_PNG, $compression=100, $permissions=null) {
      if( $image_type == IMAGETYPE_JPEG ) {
        imagejpeg($this->image,$filename,$compression);
      } elseif( $image_type == IMAGETYPE_GIF ) {
        imagegif($this->image,$filename);
      } elseif( $image_type == IMAGETYPE_PNG ) {
        imagepng($this->image,$filename);
      }
      if( $permissions != null) {
        chmod($filename,$permissions);
      }
    }

    public function output($image_type=IMAGETYPE_JPEG, $quality = 80) {
      if( $image_type == IMAGETYPE_JPEG ) {
        header("Content-type: image/jpeg");
        imagejpeg($this->image, null, $quality);
      } elseif( $image_type == IMAGETYPE_GIF ) {
        header("Content-type: image/gif");
        imagegif($this->image);
      } elseif( $image_type == IMAGETYPE_PNG ) {
        header("Content-type: image/png");
        imagepng($this->image);
      }
    }

    public function getWidth() {
      return imagesx($this->image);
    }

    public function getHeight() {
      return imagesy($this->image);
    }

    public function resizeToHeight($height) {
      $ratio = $height / $this->getHeight();
      $width = $this->getWidth() * $ratio;
      $this->resize($width,$height);
    }

    public function resizeToWidth($width) {
      $ratio = $width / $this->getWidth();
      $height = $this->getHeight() * $ratio;
      $this->resize($width,$height);
    }

    public function square($size){
      $new_image = imagecreatetruecolor($size, $size);

      if($this->getWidth() > $this->getHeight()){
        $this->resizeToHeight($size);

        imagecolortransparent($new_image, imagecolorallocate($new_image, 0, 0, 0));
        imagealphablending($new_image, false);
        imagesavealpha($new_image, true);
        imagecopy($new_image, $this->image, 0, 0, ($this->getWidth() - $size) / 2, 0, $size, $size);
      } else {
        $this->resizeToWidth($size);

        imagecolortransparent($new_image, imagecolorallocate($new_image, 0, 0, 0));
        imagealphablending($new_image, false);
        imagesavealpha($new_image, true);
        imagecopy($new_image, $this->image, 0, 0, 0, ($this->getHeight() - $size) / 2, $size, $size);
      }

      $this->image = $new_image;
    }

    public function scale($scale) {
      $width = $this->getWidth() * $scale/100;
      $height = $this->getHeight() * $scale/100;
      $this->resize($width,$height);
    }

    public function resize($width,$height) {
      $new_image = imagecreatetruecolor($width, $height);

      imagecolortransparent($new_image, imagecolorallocate($new_image, 0, 0, 0));
      imagealphablending($new_image, false);
      imagesavealpha($new_image, true);

      imagecopyresampled($new_image, $this->image, 0, 0, 0, 0, $width, $height, $this->getWidth(), $this->getHeight());
      $this->image = $new_image;
    }

    public function cut($x, $y, $width, $height){
      $new_image = imagecreatetruecolor($width, $height);

      imagecolortransparent($new_image, imagecolorallocate($new_image, 0, 0, 0));
      imagealphablending($new_image, false);
      imagesavealpha($new_image, true);

      imagecopy($new_image, $this->image, 0, 0, $x, $y, $width, $height);

      $this->image = $new_image;
    }

    public function maxarea($width, $height = null){
      $height = $height ? $height : $width;

      if($this->getWidth() > $width){
        $this->resizeToWidth($width);
      }
      if($this->getHeight() > $height){
        $this->resizeToheight($height);
      }
    }

    public function cutFromCenter($width, $height){

      if($width < $this->getWidth() && $width > $height){
        $this->resizeToWidth($width);
      }
      if($height < $this->getHeight() && $width < $height){
        $this->resizeToHeight($height);
      }

      $x = ($this->getWidth() / 2) - ($width / 2);
      $y = ($this->getHeight() / 2) - ($height / 2);

      return $this->cut($x, $y, $width, $height);
    }

    public function maxareafill($width, $height, $red = 0, $green = 0, $blue = 0){
        $this->maxarea($width, $height);
        $new_image = imagecreatetruecolor($width, $height);
        $color_fill = imagecolorallocate($new_image, $red, $green, $blue);
        imagefill($new_image, 0, 0, $color_fill);
        imagecopyresampled($new_image, $this->image, floor(($width - $this->getWidth())/2), floor(($height-$this->getHeight())/2), 0, 0, $this->getWidth(), $this->getHeight(), $this->getWidth(), $this->getHeight());
        $this->image = $new_image;
    }

  }




  /* usage
  The first example below will load a file named picture.jpg resize it to 250 pixels wide and 400 pixels high and resave it as picture2.jpg
     $image = new SimpleImage();
     $image->load('picture.jpg');
     $image->resize(250,400);
     $image->save('picture2.jpg');

  If you want to resize to a specifed width but keep the dimensions ratio the same then the script can work out the required height for you, just use the resizeToWidth function.
     $image = new SimpleImage();
     $image->load('picture.jpg');
     $image->resizeToWidth(250);
     $image->save('picture2.jpg');

  You may wish to scale an image to a specified percentage like the following which will resize the image to 50% of its original width and height
     $image = new SimpleImage();
     $image->load('picture.jpg');
     $image->scale(50);
     $image->save('picture2.jpg');

  You can of course do more than one thing at once. The following example will create two new images with heights of 200 pixels and 500 pixels
     $image = new SimpleImage();
     $image->load('picture.jpg');
     $image->resizeToHeight(500);
     $image->save('picture2.jpg');
     $image->resizeToHeight(200);
     $image->save('picture3.jpg');

  The output function lets you output the image straight to the browser without having to save the file. Its useful for on the fly thumbnail generation


  <?php
     if( isset($_POST['submit']) ) {
        include('SimpleImage.php');
        $image = new SimpleImage();
        $image->load($_FILES['uploaded_image']['tmp_name']);
        $image->resizeToWidth(150);
        $image->output();
     } else {
  ?>

     <form action="upload.php" method="post" enctype="multipart/form-data">
        <input type="file" name="uploaded_image" />
        <input type="submit" name="submit" value="Upload" />
     </form>

  <?php
     }
  ?>

  */

 

wHiTeHaT and Antonio Garcia like this

Share this post


Link to post
Share on other sites

It is a good class to generate images based upon my explanation for a list of pre-configured sizes.

Share this post


Link to post
Share on other sites

IMO it is not worth the effort and forwards compatibility problems.

If you want improved speed and you start to look at this level issue, the solution is really to throw money at it and just get better hosting.  

Share this post


Link to post
Share on other sites

@flying_kites Even if you would have super server, the images need to be loaded on the visitors device.

You could compare it as visiting a website true a 33k dial-up modem. 

Share this post


Link to post
Share on other sites

I had some deeper look on the “srcset” tag and the posibility to combine it with a thumb generator like KissIt image thumbnail.

Here my conclusions:

-    KissIt Image thumb or any other thumb generator already produces optimized image sizes for the product listings/modules and boxes.

-    As the BS responsive design changes the layout depending on the device size, the required image resizing is already very reduced.

-    In a real store with small image, heading image and subcategory image sizes of 135 - 300 px, the maximum of browser downsizing is about 10-20% of the thumb-size.

-    This is due to the layout changing in the sense that the number of columns in the product listing gets reduced and the side boxes moved to the bottom. So the image sizes are kept in a narrow range which allows one thumb to fit already very well the effective required image sizes.

-    The only images which require very different sizes are images which use almost the entire viewport width. This is in a standard installation only the store logo if using col 6-12.

-    Other images could be banners or other additional promotional images if using the main content width or placed as a header/footer content module with col 6-12.

-    Only for these examples it would make sense to create different sized thumb images for different viewport sizes.

-    I doubt if it would need to be 5 different sizes, maybe 3 could be enough.

-    Another subject would be to serve retina devices with double resolution/sized thumbs which are not addressed for now by thumb generators.

-    This would mean to generally generate a second, double sized thumb and tag it by “srcset”.

-    Thumb generator should check if the required thumb size is notably bigger than 300px width, the usual max size of a small image used in product listings and boxes.

-    Only in that case it should create various versions, depending on the size of the original image. It doesn’t make sense to upsize neither to downsize below aprox. 300 px.

-    What has much influence on the image sizes is the jpg quality setting. There would be a point to improve KissIt image thumbnailer. Until now it uses a fixed quality setting for all thumbs. It could be improved to use the quality setting of the original jpg image always if it is lower than the general setting.

-    Also to take into consideration that the store owner uses accurate jpg quality settings for the kind of his images or an external service for image optimization.

-    Just an example: a 300 px square image can vary in size by different (Photoshop) quality settings:

  •    Quality 0 => 38.4kb
  •    Quality 4 => 44.3 kb
  •    Quality 6 => 50.9 kb
  •    Quality 9 => 58.6 kb
  •    Quality 12 => 79.9 kb

-    Some resampling in the range of 10-20% can save:

  •    Original image size 300px => 58.6 kb
  •    270 px => 56 kb
  •    240 px => 52.8 kb

-    Conclusion: optimizing the images by quality/compression has the same or more influence than 100% accurate size serving.

 

 

Share this post


Link to post
Share on other sites

Hi @raiwa,

thank you so much for looking into it.

But its not only the question of the amount of data requested but also if the browser has to render (consuming loading time)  the correct size and how close it to requested result. The closer you get to the result, the less will be complaints about rendering time to cover requested resolution. So my thoughts were going this way: If there is no definition for an image size like e.g. SMALL_IMAGE_WIDTH then <picture> should jump in providing relevant resolutions  for example if you'd say  picture on product_info  should take a percentive place on certain devices.  The less scaling work a browser has to do the better the speed. 

This might be be not so much a question of the user experience, but may be a question how the search engines might classify your site.

Best regards

Christoph

Edited by beerbee

Share this post


Link to post
Share on other sites

Hi,

and now a little bit back to the original topic this thread was about.

I am totally not satisfied with something like $oscTemplate->getBlocks('footer_scripts'). This is confusing css with js all the way. In general there should be css loaded first  an then get to js scripts after. Everything should be non blocking. The trick is just mentioned before :

1
2
3
4
<style id="init-style">
*{color:#fff; border:0; background:none;}
img,button{display:none;}
</style>
is all the style gets loaded before everything else is present and looking beautiful through your css  and js efforts.
It would be much better to have a discerning loading of css and js than a mixed up one discerning footer and header.
 
Best regards
Christoph

 

Edited by beerbee

Share this post


Link to post
Share on other sites

@beerbee Come up with something. Then once you have something that works, show it to someone like Gary @burt and see whether he thinks its worth the effort of coding into the BS version. Its difficult for people to uinderstand what you are trying to get across, so show them. You never know if its adopted into the core code you will have helped advanced the project forward.

Share this post


Link to post
Share on other sites

1.  Try the script with .js turned off.  Many people have js off as default.

2.  <picture is used when you want to *force* a particular image to be rendered.  <srcset is used to reactively render.

3.  You add in database calls or logic calls in preference to simply hardcoding

It doesn't seem like a particularly valuable endeavour, but if you can make it work and prove it's better than what we have...I'm happy to consider core.

Share this post


Link to post
Share on other sites

I have

1) an image resizer that creates resized images on the fly when the requested size does not exist. Might have been KISS I'm not 100% sure - there are different ones, but essentially you get original image123.jpg and image123-120px.jpg, image123-360px.jpg kinda thing

2) adaptive images script to generate smaller / lightweight versions where appropriate - especially usefull for HD images also taking retina displays into account

http://www.adaptive-images.com/

I personally prefer the on-the-fly bit, makes it easy to make changes to image sizes without having to worry about generating all images in batch. I have been known to dump lots of images in directories, and use easypopulate to update the products_image field. A batch approach would need to be able to handle such a situation. You can not assume especially when building a new site that all images are uploaded one-by-one through an interface.

 

Share this post


Link to post
Share on other sites

@bruyndoncx I actually like the sound of the second approach. Is the installation as straightforward as they mention or are modifications required for it to work with osC? Or maybe this is massively outdated by today's web standards? 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now