# Changing the grid of field blocks

*This tutorial extends from the previous one, which explained the `$css()` utility.*

The (billing and/or shippping) address form is using CSS grids (of 12 columns) to put all fields in the right place. Each field is a Magento block (so, a `<block>` within the XML layout). And because the outer `<div>` HTML element (as found within the PHTML template for each field, like `form/field.phtml`) is using the `$css()` utility, this can be used to style each field.

## Changing the grid (the ugly way)

**Note that the following mechanisms are usable but are considered to be ugly ways to modify the grid. They work, but are ugly.**

### Changing the CSS classes directly?
Each block that uses a template with the `$css()` utility allows for CSS classes to be changed through the XML layout. For instance, the `prefix` field can be given a Tailwind classes like this:

```xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:View/Layout:etc/page_configuration.xsd">
    <body>
        <referenceBlock name="loki.checkout.shipping-step.shipping-address.prefix">
            <arguments>
                <argument name="css_classes" xsi:type="array">
                    <item name="block" xsi:type="array">
                        <item name="grid" xsi:type="string">order-2 col-span-6 md:col-span-4</item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>
```

This becomes pretty cumbersome if you need to do this for all fields in a grid. Additionally, the CSS class `order-2` applies the ordering on the CSS level, but doesn't influence the `TAB` direction, which becomes confusing in the UX.

### Changing Tailwind classes directly?
You could hack your own `order` and `col-span-*` into TailwindCSS by using `@apply`. Don't do this either: You will lack much flexibility. And it will often have undesired effects regarding accessibility.

### Moving blocks with `<move/>`?
As an alternative, you can use the XML layout `<move/>` directive to move each field in the right position. It is quickly leading to weird issues where one move in one layout update conflicts with yet another move.

Our recommendation is not to use this method for grid changes.

### Setting `before` and `after` attributes?
In a similar way, don't try to change the ordering by using `before` and `after` attributes. Perhaps this works for the first field, but once the number of fields increases and once more modules are influencing the total of the grid, this leads again into weird issues.

Our recommendation is not to use this method for grid changes.

### Changing specific XML arguments?
You can use the XML arguments `sort_order`, `col_span` and `md_col_span` for specific blocks within a grid-parent like so:

```xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:View/Layout:etc/page_configuration.xsd">
    <body>
        <referenceBlock name="loki.checkout.shipping-step.shipping-address.prefix">
            <arguments>
                <argument name="sort_order" xsi:type="number">2</argument>
                <argument name="col_span" xsi:type="number">12</argument>
                <argument name="md_col_span" xsi:type="number">6</argument>
            </arguments>
        </referenceBlock>
    </body>
</page>
```

This actually works fine and it is fully supported by the Loki core. It again becomes pretty cumbersome if you want to do this for every single block in an address. But if you only need to change a couple of blocks, this is fine.

There is a better way instead.

## Use the `etc/loki_checkout.xml` configuration file
The module `LokiCheckout_Core` ships with a file `etc/loki_checkout.xml` which configures a sorting order and CSS grid classes. This looks similar to the following:
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:LokiCheckout_:etc/loki_checkout.xsd">
    <grid_layouts>
        <grid_layout name="default_grid">
            <grid name="default">
                <block alias="prefix" sortOrder="1" colSpan="6" mdColSpan="12" />
                <block alias="firstname" sortOrder="2" colSpan="6" mdColSpan="12" />
                <block alias="middlename" sortOrder="3" colSpan="6" mdColSpan="12" />
                <block alias="lastname" sortOrder="3" colSpan="6" mdColSpan="12" />
                ...
            </grid>
        </grid_layout>
    </grid_layouts>
</config>
```

This XML configuration is used by various other mechanisms to change the HTML of the child blocks of address, like with the PHTML template `checkout/billing/address-form.phtml`.

## Making your own changes
To make changes to the grid column spans and/or the sorting order, create your own Magento 2 module and add a new file `etc/loki_checkout.xml` to it. Next, make your changes:

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:LokiCheckout_:etc/loki_checkout.xsd">
    <grid_layouts>
        <grid_layout name="default_grid">
            <grid name="default">
                <block alias="prefix" sortOrder="1" colSpan="4" mdColSpan="12" />
                <block alias="firstname" sortOrder="2" colSpan="4" mdColSpan="12" />
                <block alias="middlename" sortOrder="3" colSpan="4" mdColSpan="12" />
                <block alias="lastname" sortOrder="3" colSpan="4" mdColSpan="12" />
                ...
            </grid>
        </grid_layout>
    </grid_layouts>
</config>
```

Make sure to clean the configuration cache afterwards.

```bash
bin/magento cache:clean config
```

> We recommend you to never make these changes via CSS, but only XML. Preferably this special `etc/loki_checkout.xml` or alternatively the XML layout.

## How it works: Field width via TailwindCSS utility classes
The XML arguments `colSpan` and `mdColSpan` of the `loki_checkout.xml` file are added to every child of an address block. Commonly, these child blocks are Loki Field Components, that use a PHTML template `Loki_FieldComponents::form/field.phtml` which starts with an HTML element like the following:

```php
<div class="<?= /* @noEscape */ $css('') ?>" style="<?= $escaper->escapeHtml($style()) ?>">
</div>
```

Here, the `$css()` utility (`Loki\CssUtils\Util\CssClass`) and the `$style()` utility (`Loki\CssUtils\Util\CssStyle`) allow for dynamically adding new CSS classes and CSS styles. This is precisely done via a `LokiCheckout\Core\Util\Block\CssClassParser\GridClassParser` and a `LokiCheckout\Core\Util\Block\CssStyleParser\GridStyleParser`. The `GridClassParser` adds the CSS class `loki-col-span`. The `GridStyleParser` adds the CSS variables `--loki-col-span` and `--loki-md-col-span` with the values defined in the `loki_checkout.xml` file.

The CSS class `loki-col-span` uses these CSS variables as follows:

```css
.loki-col-span {
    --loki-col-span: 12;
    --_col: var(--loki-col-span);
    grid-column: span var(--_col) / span var(--_col);
}
```

In real life, it is a bit more complex - see the `global.css` file for the full code.

At first, this mechanism might seem complex and unwanted. The values of `colSpan` and `mdColSpan` could also be just hard-coded into the PHTML templates themselves, right? However, grids in the Loki Checkout can dynamically change, depending upon the current Store View, the selected country and custom logic. The grid determines the field width, not the other way around. 

Additionally, the usage of CSS variables is complex as well, but it avoids declaring every possible combination of `col-span-*` in the Tailwind build. It is better for performance.

## How it works: Sort order of fields
The XML argument `sortOrder` of the `loki_checkout.xml` is, at first, picked up by another mechanism. However, in the end, it also uses the same approach of CSS classes and CSS variables.

First, the observer `LokiCheckout\Core\Observer\SetSortOrder` takes the XML argument `sortOrder` and sets this as a block argument `sort_order` (aka `getSortOrder()` / `setSortOrder()`).

Next, the PHTML template `checkout/billing/address-form.phtml` (for example) renders all child blocks (so, all fields) as follows:

```php
use Loki\Components\Util\Block\ChildRenderer;
use Loki\CssUtils\Util\CssClass;
use Magento\Framework\Escaper;
use Magento\Framework\View\Element\Template;

/** @var Template $block */
/** @var Escaper $escaper */
/** @var CssClass $css */
/** @var ChildRenderer $childRenderer */

?>
<div class="<?= $escaper->escapeHtml($css('grid md:grid-cols-12 my-8 gap-4 md:gap-8')) ?>">
    <?= /* @noEscape */ $childRenderer->all($block) ?>
</div>
```

Most importantly, the `$childRenderer->all()` is used (instead of a more regular `$block->getChildHtml()`). This renderer makes sure that all blocks are ordered by their `sort_order` before rendering. The HTML, as rendered by the browser, contains the right ordering of blocks.

On top of this, the `GridClassParser` also add a CSS classes `order-(--loki-sort-order)` to each child block (aka, each field). The Tailwind CSS class `order-(--x)` turns the `--x` (the CSS variable) into the value of the CSS rule `order`:

```css
order: 42;
```

> Under TailwindCSS 3, the CSS class `order-[--loki-sort-order]` is used instead

It is important to understand that this sets a CSS `order` which normally positions the block in the same way as it is position in the HTML. At first, this makes little sense. However, note that Loki Field Components can update their own value and then re-render other parts of the HTML as well. With this tech in mind, a custom implementation could re-order the tax field if the company field is filled. And because those component updates are AJAX-driven, the Loki Checkout changes the CSS `order` via JavaScript. It is a niche scenario but fully supported by the Loki Checkout.

> If your fields move unexpectedly in ordering, after the document is loaded, most likely the JavaScript mechanism reveals that your TailwindCSS build has an issue: Your CSS should include a class `order-(--loki-sort-order)`.

## Different grid layouts
The default grid layout is called `default_grid`, but other grid layouts can be created as well. For instance, the `LokiCheckout_Nl` module introduces a grid layout called `dutch_grid` and the `LokiCheckout_PostcodeNl` modules introduces a grid layout called `postcode_nl_grid`. Just search for any `etc/loki_checkout.xml` file in your system.

You can also add your own grid layout if you want by adding a `etc/loki_checkout.xml` to a custom Magento module:
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:LokiCheckout_:etc/loki_checkout.xsd">
    <grid_layouts>
        <grid_layout name="custom_grid">
            ...
        </grid_layout>
    </grid_layouts>
</config>
```

The grid layout is applied to a certain form via the XML layout.

For instance, the layout file `loki_checkout_block_billing_address.xml` could look as follows:
```xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:View/Layout:etc/page_configuration.xsd">
    <body>
        <referenceBlock name="loki-checkout.billing-address.address-form">
            <arguments>
                <argument name="grid_layout" xsi:type="string">custom_grid</argument>
            </arguments>
        </referenceBlock>
    </body>
</page>
```

And similarly, the layout file `loki_checkout_block_shipping_address.xml` could look as follows:
```xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:View/Layout:etc/page_configuration.xsd">
    <body>
        <referenceBlock name="loki-checkout.shipping-address.address-form">
            <arguments>
                <argument name="grid_layout" xsi:type="string">custom_grid</argument>
            </arguments>
        </referenceBlock>
    </body>
</page>
```

## Grids per country
The `etc/loki_checkoutxml` file also allows for a grid layout to have different settings per country. For instance, take the following snippet from the core:

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<config>
    <grid_layouts>
        <grid_layout name="default_grid" defaultMdColSpan="6">
            <grid name="default">
                ...
                <block alias="country_id" sortOrder="120" />
                ...
            </grid>
            <grid name="netherlands" countryId="NL" parent="default">
                <block alias="country_id" mdColSpan="12"/>
            </grid>
        </grid_layout>
    </grid_layouts>
</config>
```

Here, the grid layout `default_grid` contains a grid `default` that defines the country field to have a `md:col-span-6` CSS class. However, when the country is `NL` (The Netherlands), this changes to `md:col-span-12`.

## About the Tailwind `order-*` classes
Each field is ordered within the HTML following these grid layout settings. In other words, the default HTML document resembles the final ordering of all blocks. That being said, the same HTML contains Tailwind `order-*` classes.

These CSS classes only serve a single purpose: When the country is changed (for instance, from Germany to The Netherlands), the grid settings could lead into a different layout. 

## About the `loki-checkout.shipping.address.form.grid-layout` component
One of the child-blocks of an address grid is the block `loki-checkout.shipping.address.form.grid-layout`. This block takes in a special place: It renders as an empty `div`, but because it is registered as a Loki Components, its Alpine.js code is run automatically. Within this JavaScript logic, the sorting order of each field in the grid is taken to sort the actual HTML within the grid. 

This makes sure that - for instance, when the country is changed and therefore the grid fields should be sorted in a different way - the `TAB` navigation matches the visual ordering as determined with the Tailwind `order-*` classes.
