Adding values from custom model as CSS in templates
Question
I have a module that needs to display specific CSS values for each entry in my custom model collection.
This is trivial enough, as I can simply echo these values with PHP in my template as needed; however, I can't get over the fact that this requires me to plant an embedded style sheet in my template.
I'm essentially doing:
<?php $models = Mage::getModel('my/model')->getCollection() ?>
<style type="text/css">
[class*='item-'] {
display: block;
padding: 20px;
}
<?php foreach ($models as $model): ?>
.item-<?php echo $model->getId() ?> {
background: <?php echo $model->getMyColor() ?>;
}
<?php endforeach ?>
</style>
<ul>
<?php foreach ($models as $model): ?>
<li class="item-<?php echo $model->getId() ?>">
<p><?php echo $model->getText() ?></p>
</li>
<?php endforeach ?>
</ul>
The purist in me really doesn't like this approach. I start to twitch when I see the markup mixed with PHP and CSS like that... though, in all fairness, it does what it needs to do.
Is there a better approach I am missing? While I am comfortable using SASS or other CSS preprocessors, I can't rely on these being present in a random server environment.
What methods do you use to inject PHP variables into your stylesheets?
Solution
You shouldn't be trying to modify CSS with PHP, you have a other options. Without knowing that much about your module it is hard to give specific help but here are a couple of ideas:
1 - Add each colour as a class
<ul>
<?php foreach ($models as $model): ?>
<li class="item-<?php echo $model->getMyColour() ?>">
<p><?php echo $model->getText() ?></p>
</li>
<?php endforeach ?>
</ul>
2 - Add an inline style to the element
<ul>
<?php foreach ($models as $model): ?>
<li class="item-<?php echo $model->getId() ?>"
style="background-color:#<?php echo $model->getMyColor();?>">
<p><?php echo $model->getText() ?></p>
</li>
<?php endforeach ?>
</ul>
Inline styles are perfectly acceptable for one-off styles, which sounds like what you're after. This style will not be re-used anywhere else and the individual rules might be too numerous to code.
OTHER TIPS
If your style is dynamic, why not use inline styles. I know it's not clean, but it's cleaner than adding a style tag in your template. Just add in one of your css files this:
.custom-item {
display: block;
padding: 20px;
}
IN this class you can add all the common properties for your items and in the template just do this:
<?php foreach ($models as $model): ?>
<?php $color = $model->getMyColor()?>
<li class="custom-item"<?php if ($color) : ?> style="background:<?php echo $color;?>"<?php endif;?>>
<p><?php echo $model->getText() ?></p>
</li>
<?php endforeach ?>
I ended up adding some helper methods for various items. What was unappealing about the inline method is that, while it's fine for a random display: none;
here or there, or other basic rules, it's not appropriate to plaster your markup with complex CSS in inline styles.
What I have now is:
<ul>
<?php foreach ($models as $model): ?>
<!-- Inline styles for each item -->
<?php $styles = $myHelper->getListInlineCSS($model->getId()) ?>
<li class="item-<?php echo $model->getId() ?>" style="<?php echo $styles ?>">
<p><?php echo $model->getText() ?></p>
</li>
<?php endforeach ?>
</ul>
The helper method looks like:
public function getListInlineCSS($id)
{
$inlineCSS = '';
// Getting colors from each model using these methods that support returning rgba values as well as rgb values for IE < 9 support.
$inlineCSS .= 'background: ' . $this->textBoxBg($id, true) . '; '; // returns rgb(xxx,xxx,xxx)
$inlineCSS .= 'background: ' . $this->textBoxBg($id) . '; '; // returns rgba(xxx,xxx,xxx,xxx)
$inlineCSS .= 'color: ' . $this->textBoxText($id, true) . '; ';
$inlineCSS .= 'color: ' . $this->textBoxText($id) . '; ';
return $inlineCSS;
}
This is a pretty basic form of what I was after, so I will likely expand on this to handle many different elements appropriately.
Every page has his own layout-handle. This way you can add a css file exactly to this one page, for examle:
<catalog_product_compare_index translate="label">
<action method="addItem"><type>skin_css</type><name>css/styles-ie.css</name><params/><if>lt IE 8</if></action>
</catalog_product_compare_index>
You can leave out the <if>
statement if you want.
Also, the HTML tag has this layout handle added as a class name. See `page/1column.phtml' template for an example:
...
<body<?php echo $this->getBodyClass()?' class="'.$this->getBodyClass().'"':'' ?>>
....
The resulting markup might be something like:
<body class="cms-index-index cms-home">
Using these two methods you can easily style elements on a per-page basis.