15
\$\begingroup\$

One day, I was thinking:

Wouldn't it be nice to set a class and have all the styles defined?

And that is exactly what I did: a PHP file that generates CSS with pre-defined styles. So, for fun, I built the following:

<?php

    if( @is_file($file = basename(__FILE__, '.css.php') . '-config.php' ) )
    {
        $config = (array)include( $file );
    }
    else
    {

        $config = array(
            'class'=>'color',
            'force'=>true,
            'text'=>true,
            'border'=>true,
            'back'=>true,
            'shadow'=>true,
            'sizes'=>true,
            'styles'=>true,
            'send_header'=>true,
            'custom'=>null
        );

    }

    if( isset( $config['send_header'] ) && $config['send_header'] && !headers_sent() )
    {
        header('Content-type: text/css');
    }

    $colors = array(
        'black',
        'red'=>array('dark','indian','mediumviolet','orange','paleviolet'),
        'green'=>array('dark','light','forest','yellow','lawn','lime','pale','darkolive','sea','darksea','lightsea','mediumsea','spring','mediumspring'),
        'blue'=>array('alice','cadet','cornflower','dark','darkslate','deepsky','dodge','light','lightsky','lightsteel','medium','mediumslate','midnight','powder','royal','sky','slate','steel'),
        'white'
    );

    $sizes=array(
        'em'=>array(0.3,0.5,1,1.25,1.3,2,2.5,3,3.3,3.5,4),
        'px'=>array(0,1,2,3,4,5,6,7,8,9,10,11,12,12.5,13,14,15,16,17,17.5,18,19,20),
        'mm'=>range(0,9),
        'cm'=>range(0,9),
        '%'=>array(0,1,2,3,4,5,6,7,8,9,10,12.5,15,20,25,30,33.3,35,40,45,50,55,60,62.5,65,66.6,65,70,75,80,85,90,95,99,100)
    );

    $styles=array(
        'border'=>array('none','doted','dashed','solid'),
        'text'=>array('none','underline')
    );

    $important = ( isset($config['force']) && $config['force'] ? '!important' : '' );
    $class = ( !isset($config['class']) || $config['class'] === null ? '.color' : ( $config['class'] == false ? '' : '.'.$config['class'] ) );

    foreach($colors as $color_name=>$color)
    {
        if( is_array($color) )
        {
            foreach( $color as $sub_color )
            {               
                if( isset($config['text']) && $config['text'] )
                {
                    echo $class, '.', $color_name, '-', $sub_color, '-text{color:', $sub_color, $color_name, $important, ';}';
                }

                if( isset($config['border']) && $config['border'] )
                {
                    echo $class, '.', $color_name, '-', $sub_color, '-border{border-color:', $sub_color, $color_name, $important, ';}';
                }

                if( isset($config['back']) && $config['back'] )
                {
                    echo $class, '.', $color_name, '-', $sub_color, '-back{background-color:', $sub_color, $color_name, $important, ';}';
                    echo $class, '.', $color_name, '-', $sub_color, '-background{background-color:', $sub_color, $color_name, $important, ';}';
                }

                if( isset($config['shadow']) && $config['shadow'] )
                {
                    echo $class, '.', $color_name, '-', $sub_color, '-shadow{text-shadow-color:', $sub_color, $color_name, $important, ';box-shadow-color:', $sub_color, $important, ';}';
                }

                if( isset($config['outline']) && $config['outline'] )
                {
                    echo $class, '.', $color_name, '-', $sub_color, '-outline{outline-color:', $sub_color, $color_name, $important, ';}';
                }
            }
        }
        else
        {

            if( isset($config['text']) && $config['text'] )
            {
                echo $class, '.', $color, '-text{color:', $color, $important, ';}';
            }

            if( isset($config['border']) && $config['border'] )
            {
                echo $class, '.', $color, '-border{border-color:', $color, $important, ';}';
            }

            if( isset($config['back']) && $config['back'] )
            {
                echo $class, '.', $color, '-back{background-color:', $color, $important, ';}';
                echo $class, '.', $color, '-background{background-color:', $color, $important, ';}';
            }

            if( isset($config['shadow']) && $config['shadow'] )
            {
                echo $class, '.', $color, '-shadow{text-shadow-color:', $color, $important, ';box-shadow-color:', $color, $important, ';}';
            }

            if( isset($config['outline']) && $config['outline'] )
            {
                echo $class, '.', $color, '-outline{outline-color:', $color, $important, ';}';
            }
        }
    }
    unset($colors,$color,$color_name,$sub_color,$sub_color_name);

    if( isset($config['custom']) )
    {
        foreach( $config['custom'] as $color=>$hex )
        {
            if( isset($config['text']) && $config['text'] )
            {
                echo $class, '.custom-', $color, '-text{color:#', $hex, $important, ';}';
            }

            if( isset($config['border']) && $config['border'] )
            {
                echo $class, '.custom-', $color, '-border{border-color:#', $hex, $important, ';}';
            }

            if( isset($config['back']) && $config['back'] )
            {
                echo $class, '.custom-', $color, '-back{background-color:#', $hex, $important, ';}';
                echo $class, '.custom-', $color, '-background{background-color:#', $hex, $important, ';}';
            }

            if( isset($config['shadow']) && $config['shadow'] )
            {
                echo $class, '.custom-', $color, '-shadow{text-shadow-color:#', $hex, $important, ';box-shadow-color:#', $hex, $important, ';}';
            }

            if( isset($config['outline']) && $config['outline'] )
            {
                echo $class, '.custom-', $color, '-outline{outline-color:#', $hex, $important, ';}';
            }
        }
    }
    unset($color,$hex);

    if( isset($config['sizes']) && $config['sizes'])
    {
        foreach($sizes as $size_name=>$size_list)
        {
            foreach($size_list as $size_value)
            {
                if( isset($config['text']) && $config['text'] )
                {
                    echo '.text-', str_replace('.', '_', $size_value), $size_name, '{font-size:', $size_value, $size_name, $important, ';}';
                }
                if( isset($config['border']) && $config['border'] )
                {
                    echo '.border-', str_replace('.', '_', $size_value), $size_name, '{border-width:', $size_value, $size_name, $important, ';}';
                }
            }
        }
    }
    unset($sizes,$size_name,$size_list,$size_value);

    if( isset($config['styles']) && $config['styles'])
    {
        if( isset($config['text']) && $config['text'] )
        {
            foreach($styles['text'] as $style)
            {
                echo '.text-',$style,'{text-decoration:',$style,$important,';}';
            }
        }
        if( isset($config['border']) && $config['border'] )
        {
            foreach($styles['border'] as $style)
            {
                echo '.border-',$style,'{border-style:',$style,$important,';}';
            }
        }
    }

    unset($styles,$style,$config,$important,$class);

It is a lot of code.

The color list isn't finished (there are tons of colors missing), but the functionality is all there.

Everything is customizable.
To set other settings, without changing code, save this file as <name>.css.php, and create a file called <name>-config.php.
It will be detected automatically and the settings will be loaded.

Example of a file with the settings:

    return array(
        'class'=>'color',
        'force'=>true,
        'text'=>true,
        'border'=>true,
        'back'=>true,
        'shadow'=>true,
        'sizes'=>true,
        'styles'=>true,
        'send_header'=>true,
        'custom'=>array()
    );

Each setting in detail:

  • 'class'
    Class that must be used for colors.
  • 'force'
    Forces the styles, by applying !important.
  • 'text'
    Defines if it is to send the styles and colors to apply to texts.
  • 'border'
    Defines if it is to send the styles and colors to apply to borders.
  • 'back'
    Defines if it is to send the background colors.
  • 'shadow'
    Defines if it is to send the shadow colors.
  • 'sizes'
    Defines if it is to send the sizes for text and borders.
  • 'styles'
    Defines if it is to send the styles for text and borders.
  • 'send_header'
    Defines if it is to send the header Content-type: text/css. - 'custom'
    Defines new colors. To avoid problems, these will have the name <class>.custom-<color>.
    This works with the color name as the key and the value as the hexadecial representation without #.
    E.g.: array('gold'=>'CFB53B') (will produce, for example, .color.custom-gold-<style>{color:#CFB53B!important;})

How to use it:

Simply call it like any CSS file:

<link href="quickstyle.css.php" rel="stylesheet" type="text/css"/>

Or include it (with the option send_header set to false):

<style><?php include 'quickstyle.css.php'; ?></style>

How to use the classes:

Simply set them to your heart's content:

<div class="color black-background white-text blue-border border-2px border-solid">Hello World!</div>

Which would look like this:

<div style="background:black;color:white;border:2px solid blue;">Hello World!</div>

(Disregard the choice of color, please)

In terms of readability, features and performance, what else can I change or improve?

\$\endgroup\$
  • 4
    \$\begingroup\$ I am in the process of filtering out some of the comments here. .... hmmm. in fact, I am going to purge them all, and invite people to chat about this in the 2nd monitor chat room. \$\endgroup\$ – rolfl May 4 '15 at 14:29
  • \$\begingroup\$ May you say if there is any advantage with this method over conventional in-line styling? \$\endgroup\$ – Ormoz May 10 '15 at 10:11
  • \$\begingroup\$ @Ormoz Most likely not. That is debatable. Personaly, the only advantage I see is that you have a more semanthic set of classes. This was simply made for fun, nothing else. \$\endgroup\$ – Ismael Miguel May 10 '15 at 16:04
10
+50
\$\begingroup\$

@cimmanon addressed some very good points on the concept itself, so I thought I would address the code.

$colors

You assume that all we want is colors in hexadecimal. But what about rgba and hsl?

You should change how the colors are made, so that you detect when a color is missing the # and add it accordingly.


$sizes

$sizes=array(
    'em'=>array(0.3,0.5,1,1.25,1.3,2,2.5,3,3.3,3.5,4),
    'px'=>array(0,1,2,3,4,5,6,7,8,9,10,11,12,12.5,13,14,15,16,17,17.5,18,19,20),
    'mm'=>range(0,9),
    'cm'=>range(0,9),
    '%'=>array(0,1,2,3,4,5,6,7,8,9,10,12.5,15,20,25,30,33.3,35,40,45,50,55,60,62.5,65,66.6,65,70,75,80,85,90,95,99,100)
);

This is also not very flexible, since all the values are hard-coded into the inner arrays.

What if I wanted to make something 24px, or 42% or 110%? I think you should make something that accepts any integer and/or decimal value then have a function to concatenate the extension (like em, px, %) after it.

It could also be good to define a reasonable range, but the good thing with CSS is if you enter a ridiculous value, like -50% or 99999999px it will either just not display, or look really weird.


$styles

$styles=array(
    'border'=>array('none','doted','dashed','solid'),
    'text'=>array('none','underline')
);

You have a typo that could lead to weird bugs. It should be dotted, not doted.


I think other than that, it's a pretty creative idea and is pretty well executed.

\$\endgroup\$
  • \$\begingroup\$ About the colors, you can add more custom colors using the configuration file. About the sizes, you are correct that it isn't flexible at all. I could add an option where we can set an array with custom sizes. About the styles, you are totally correct and I didn't noticed that tiny typo. About the !important, that is configurable in the configuration file, using the option 'force' (set it to false and bye-bye !important). \$\endgroup\$ – Ismael Miguel Jun 12 '15 at 17:45
  • 1
    \$\begingroup\$ I apologize that I did not read the details of the settings very closely. I will edit my question to rectify. \$\endgroup\$ – Phrancis Jun 15 '15 at 18:32
  • \$\begingroup\$ @IsmaelMiguel If you have a new suggestion to add, please write an independent answer of your own. \$\endgroup\$ – 200_success Jun 16 '15 at 17:38
  • \$\begingroup\$ @200_success Read the chat \$\endgroup\$ – Ismael Miguel Jun 16 '15 at 17:40
19
\$\begingroup\$

As a CSS author, I am horrified by the very concept of this code. There are very goods reasons why we don't use inline styles:

  • They cannot be reused from one document to the next like an external stylesheet can (ie. they increase page weight if you have more than 1 document)
  • They are a maintenance nightmare (oh, you want that doodad to be a different color now? have fun fixing it on every single page)

Your application does essentially the same thing, but with classes instead of the style attribute. This goes against over 15 years worth of CSS best practices. There should never be in instance where you have class names that reflects what the element should look like.

You know what this reminds me of? Libraries that programatically generate SQL for you. It looks simple to do at first, then you start realize how many features you have to write interfaces for (recursive queries, CTE, window functions, etc.). At the end of the day, you end up having to write your most complicated queries the old fashioned way, wondering why you're even bothering to use that library in the first place when it only solves your simplest use cases.

\$\endgroup\$
8
\$\begingroup\$

Code review

There are too many things going on in this line:

if( @is_file($file = basename(__FILE__, '.css.php') . '-config.php' ) )
  • Formulate a file path from __FILE__, chopping off .css.php and appending -config.php
  • Store the path in a variable
  • Check if the path exists

It's hard to read when a single line does so much. It's better to break this up to two statements:

$file = basename(__FILE__, '.css.php') . '-config.php';
if (is_file($file)) {

The code is full of tedious isset($config['x']) && $config['x'] checks. To make that simpler, I would start with an array of default values, and copy over the parameter array, which will ensure that all values exist, so that you could skip all the isset checks.


I wonder what all the unset statements are about. I checked a few, and they don't seem to matter, as the values they unset are either not used later, or reassigned. They seem pointless (and paranoid), so you can remove them.

More reasons why this is a bad idea

@cimmanon mention some reasons, for me these are the biggest:

  • The main objective of utility code should be to reduce complexity. This code, unfortunately, is doing the opposite, by adding another API layer for working with styles. CSS alone is hard enough to figure out and debug. To use your generator, I have to learn another API (your configuration logic), and if something doesn't work as expected, I may have to debug this extra API in addition to plain old CSS.

  • If you want to generate CSS, look into frameworks designed for this, for example SASS, LESS, or one of the many other similar tools that are new and sexy and I've never even heard about. Like your tool, these too add another API layer, but that's ok, because they add massive value on top of CSS.

  • I see some usability issues. Let's say you have 3 pages that use slightly different style sheets. How will that work with this script? It seems you would need 3 copies of the script. Or better, use symlinks. Even so, it seems tedious compared using CSS alone and no extra PHP files per custom style.

  • CSS files can be zipped, which can be very important for sites where performance is paramount. (The frameworks I mentioned earlier help with that too.) This is in contrast with CSS generated on the fly. To get that zipped, you'd probably have to save to a file first, which is yet another hoop to jump through.

\$\endgroup\$
  • \$\begingroup\$ You made really good points about the code. The function is_file will give a warning if the file doesn't exist (the configuration file doesn't have to exist) that function triggers a warning. I use that function because it is faster than file_exists. Also, css files are gzipped by the server automatically. There's nothing I should do about it. All the other points I agree at 100%. \$\endgroup\$ – Ismael Miguel Jun 14 '15 at 7:04
  • \$\begingroup\$ Yeah, that's why I had a feeling about @is_file. Dropped that point. About zipping CSS, my point was that your generated CSS has a disadvantage over plain CSS files: the plain files can be zipped, but CSS generated on the fly probably not so simple \$\endgroup\$ – janos Jun 14 '15 at 7:09
  • \$\begingroup\$ You can always generate the css once and serve it statically. Obviously, it isn't the best option, but it is one. \$\endgroup\$ – Ismael Miguel Jun 14 '15 at 14:55
  • \$\begingroup\$ Sure you can, but that's yet another hoop to jump through. \$\endgroup\$ – janos Jun 16 '15 at 13:36
  • \$\begingroup\$ That's totally true, and I don't disagree. \$\endgroup\$ – Ismael Miguel Jun 16 '15 at 14:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.