Developing with css inline
Tags: php, webdev, css
Published: 2023-03-17 12:28:31

First all, dont try this technic free. I use this technic when not use WebPack :

```ssh
composer require symfony/webpack-encore-bundle
``` 

I hate use dependences for my projects when, you can like backend developer, write inline inside **style** tag, and then, remove them from the code and compress on fly.

let's see how...


This is:

```php

class CssCompressService {

    public function compress(string $html): Response
    {
        $buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '',     $this->getCssInline($html));

        $buffer = str_replace(': ', ':', $buffer);

        $buffer = str_replace(array("\r\n", "\r", "\n", "\t", '  ', '    ', '    '), '', $buffer);

        ob_start("ob_gzhandler");

        $response = new Response($buffer);
        $response->headers->set('Content-type',' text/css');

        return $response;
    }

    private function getCssInline($html) : string
    {
        preg_match_all('/<style[^>]*>([^<]+)<\/style>/', $html, $matches, PREG_OFFSET_CAPTURE);

        $css = '' ;
        foreach ($matches[0] as $match) {
            $css .= strip_tags($match[0]) ;
        }

        return $css;
    }
}
```

then you have to add this tag inside your head:

```html

    <link href="{{ path(app.request.get('_route'),app.request.get('_route_params')) }}?compress=true" rel="stylesheet">
``` 

the twig function **app.request.get** allow get the name of your Controller that intercept your request and the params of your request. This way, you dont need write the path of your Controller each time.

Inside your Controller, you only need add this snippet of code:

```php

if($this->requestStack->getCurrentRequest()->query->has('compress')){
    return $this->cssCompressService->compress($html);
}
```      

Now, we need remove the **style** tag from the html code rendered. We can use the **DomDocument** class for get the **style** nodes and remove them.

```php

class DomService {

    public function __construct(private readonly TidyService $tidyService){}

    public function output(string $html): Response
    {
        $dom = new DOMDocument('1.0', 'UTF-8');
        @$dom->loadHTML($html);
        $script_tags = $dom->getElementsByTagName('style');

        while($script_tags->item(0) != null) {
            if(is_object($script_tags->item(0)->parentNode)) {
                $script_tags->item(0)->parentNode->removeChild($script_tags->item(0));
            }
        }
        return $this->tidyService->parse(new Response($dom->saveHTML()));

    }

}
```

Pay attention on the **constructor** class. We are injected the TidyService. A class that use the [Tidy](https://www.php.net/manual/es/book.tidy.php) extension php, for sort the html rendered. Very beautifull.... 

And our Controller is as follow:

```php

 #[Route('/blog/{page}', name: 'app_blog')]
    public function blog(
        PostRepository $postRepository,
        int $page = 1
    ): Response
    {
        $this->seoMetaTagsService->generate(
            $this->requestStack->getCurrentRequest(),
            true,
            [],
            'Blog',
            ''
        );

        $html = $this->render('blog/index.html.twig', [
            'posts_results_paginated' => $postRepository->paginate($page),
            'posts_results_all' => $postRepository->findAll()
        ])->getContent() ;
        if($this->requestStack->getCurrentRequest()->query->has('compress')){
            return $this->cssCompressService->compress($html);
        }

        return $this->domService->output($html);

    }
```

With this way, you can write css inline as follow:
```php

{% macro print(skill,loop) %}
    {% if loop.first %}
    <style>
        td a {
            color: #1c262f;
        }
        td a:hover {
            color: #46a80f;
            text-decoration: none;
        }
        td i {
            cursor: pointer;
        }
    </style>
    {% endif %}
    <tr>
        <td><strong>{{ skill.name }}</strong></td>
        <td>{{ skill.description }}</td>
        <td style="width: 100px">
            <a href="{{ path('skill_update', {'skillId': skill.id }) }}" title="update skill">
                <i class="fa fa-edit"></i>
            </a>
            <a href="{{ path('skill_delete', {'skillId': skill.id }) }}" title="remove skill">
                <i class="fa fa-eraser"></i>
            </a>
        </td>
    </tr>
{% endmacro %}
```

In our macros from Twig, you can write css inline, without fear that the style tag embbebed on your html code. Your css code will be compress and when you do a request to the controller, you will have a response with the css compressed  like that :

```css

body {background-color:#1c262f;}.navbar.top-nav-collapse {border-bottom:1px dashed darkseagreen;}.active > .page-link, .page-link.active {background-color:#1c262f;border-color:#1c262f;}.page-link {color:#1c262f;}.page-link:hover {color:seagreen;}.dataTables_wrapper .dataTables_length select {border:1px solid black;}.dataTables_wrapper .dataTables_filter input {border:1px solid black;}button {padding:0 15px 0 15px;}button i {padding:5px ;}table#datatable {background-color:#c1d6cc;}body {background-color:darkseagreen;}section#posts {margin-top:5em;}td a {color:#1c262f;}td a:hover {color:#46a80f;text-decoration:none;}td i {cursor:pointer;}td img {border:1px solid darkslategrey;}
```

## Some considerations
 - When you put css inside your macros, you should add the **loop** variable if you are using a loop, because the style css inline will be repeated.
 - You will have two requests. One, the request with the **compress** parameter that render your css inline compress without tools like WebPack, UglyFy, CSS compresors, etc etc, and the normal request that render your template **Twig**.
 - You can use the style tags as many times as you want. They will be removed from your code and injected on the fly in the header.
 - If you know about css, you can use the specificity for avoid css style colisions more easy. Each page, macro, template,,, have own css.