Order items with CSS on specific devices

CSS order items

CSS property order can be used to order elements within a flexbox. You can reverse the order of items inside a flexbox by applying flex-direction: row-reverse; to the flexbox.

If you want a flex-item to appear as the first element, you apply order: -1; to that item.

However.. If you want to place the 5th item after the 6th item, you'll have to manually set an order for every single flex-item in the flexbox.. Otherwise, other flex-items will appear as the first items inside of the flexbox. You'll have to add an order to new flex-items too.

You might want to order items differently based on what device is being used, but you don't want to repeat writing CSS and new classes/IDs for future cases where you want to reorder a specific item inside a specific flexbox on a specific device.

Using JavaScript for this, is not the best solution either, because it causes reflows.

If you want a solution for this.. then keep reading, because I have a solution for you.


Solution for device-specific order

The solution I have for you uses CSS and PHP, no JavaScript. This is the CSS you need:

  @media (max-width: 480px) {
    [style*='--om:'] {
      --om:	0;	/* order mobile */
      --mm:	0;	/* move mobile */
      --mdm:	0;	/* move direction mobile */
      
      order:	calc(var(--om) + var(--mm) + var(--mdm));
    }
  }
  @media (min-width: 481px) and (max-width: 1024px) {
    [style*='--ot:'] {
      --ot:	0;	/* order tablet */
      --mt:	0;	/* move tablet */
      --mdt:	0;	/* move direction tablet */
      
      order:	calc(var(--ot) + var(--mt) + var(--mdt));
    }
  }
  @media (min-width: 1025px) {
    [style*='--od:'] {
      --od:	0;	/* order desktop */
      --md:	0;	/* move desktop */
      --mdd:	0;	/* move direction desktop */
      
      order:	calc(var(--od) + var(--md) + var(--mdd));
    }
  }

Don't forget to change the media queries to how you target mobile devices, tablet devices and desktop devices. This is the only CSS that you need, nothing else.

Now, the PHP code. The first thing you have to do is get all the HTML by calling ob_start() before adding any HTML and getting the HTML by calling ob_get_clean() after all HTML is echoed, printed, etc. You are going to use PHP's XML/HTML parser.

Create a DOMDocument and load the HTML that you've received from the buffer:

$doc = new DOMDocument();
$doc -> loadHTML($html);
$xPath = new DOMXPath($doc);

The DOMXPath will be used to perform complex queries to find specific HTML-elements. Now you need a function that gives flex-items a value for the order CSS property:

function numberFlexItems($flexbox, $device) {
  $flexItemsToNumber = [];
  $foundMovingItem = false;
  $order = 0;
  
  foreach($flexbox -> childNodes as $flexItem) {
    if($flexItem -> nodeType == XML_ELEMENT_NODE) {
    
      array_push (
        $flexItemsToNumber,
        [
          $flexItem,
          $order
        ]
      );
      
      if($flexItem -> hasAttribute('data-m-' . substr($device, 0, 1))) {
        
        $move = intval($flexItem -> getAttribute('data-m-' . substr($device, 0, 1)));
        if($move != 0) {
          $foundMovingItem = true;
          
          if($move > 0) {
            $moveDirection = 1;
          } else {
            $moveDirection = -1;
          }
          
          $flexItem -> setAttribute (
            'style',
            '--md' .  substr($device, 0, 1) . ':' . $moveDirection . ';'
            . '--m' .  substr($device, 0, 1) . ':' . $move . ';'
            . $flexItem -> getAttribute('style')
          );
        }
        
      }
      
      $order++;
    }
  }
  
  if($foundMovingItem) {
    foreach($flexItemsToNumber as $flexItemAndNumber) {
      
      $flexItemAndNumber[0] -> setAttribute (
        'style',
        '--o' . substr($device, 0, 1) . ':' . $flexItemAndNumber[1] . ';'
        . $flexItemAndNumber[0] -> getAttribute('style')
      );
    }
  }
}

After you've copy-pasted this into your PHP-code, you'll need to call this function on your flexboxes. This function will number flex-items for you and set CSS variables for specific items to be moved on specific devices. You need to call this function for the types of devices for which you want to specify a seperate order for flex-items.

$flexboxes = $xPath -> query('//*[contains(@class, \'flexbox\')]');  // replace flexbox with the classname that you use for flexboxes
foreach($flexboxes as $flexbox) {
  numberFlexItems($flexbox, 'desktop');
  numberFlexItems($flexbox, 'tablet');
  numberFlexItems($flexbox, 'mobile');
}
echo '<!DOCTYPE html>' . $doc -> saveHTML($doc -> documentElement); // echo the final HTML

That's it! This is all you need to be able to order elements inside of a flexbox, without lots of manual work, additional CSS, classes, or JavaScript.

How to use the solution?

If you want to place the 5th flex-item after the 6th item on a tablet device, just add the following HTML-attribute to the 5th item: data-m-t="1".

The order is increased by 1 on tablet devices. It is actually increased by 2, because else the order would be the same as the 6th item. If the order of two flex-items are equal, then the actual order inside of the flexbox is taken into account; thus placing item 5 before 6.

Note that character t in data-m-t means tablet. The last character of the attribute's name is the first character of a specific device at which you want to order a flex-item.


This solution is simplified. The additional functionality, which I am sure only few need, has been cut out of the PHP code. This simplified solution has been tested on the homepage of this website. I hope that this solution added more flexibility to your design.

>