CSS init
the corz.org CSS "pre-processor"
Not static, baby!
When I first started doing CSS, I quickly realised that it was full of limitations and so generated it with php, working around browser bugs, switching color scheme values with replaceable %%tokens%%, sending values with includes, modularizing repetative code and so on.
Over the years the init has picked up a few capabilities, simple mathematical expressions, minification and more, and has become a fairly powerful CSS "pre-processor" (a term that didn't even exist when I first started doing this back in 2004!).
Okay, how?
The first thing you need to do is setup your web server to handle the .css files differently. I'll give examples for Apache, because that's what most people use; but similar things are doable in most web servers (and usually with the same code!).
Simply put, we tell the web server to not spit out the files as-is, but instead pipe them through php, so it can have its wicked way with them.
In your ".htaccess" file for your css folder, add this..
<FilesMatch "\.(css|style)$">
SetHandler application/x-httpd-php
</FilesMatch>
From now on, all files with a .css
or .style
extension, will be parsed by php, and treated as code.
Now for the code..
Init, innit?
Before we start spitting out individual CSS, there is some code which will be common to all the.css
files. It makes sense to keep this in a single .php
file, save having to code it into every one. We simply include it from the .css
files.
css-init.php
does the basic setup common to all the stylesheets, as well as quite a bit of magic, here it is..
<?php /* --- ۞---> text { encoding:utf-8;bom:no;linebreaks:unix;tabs:4sp; } */
if (realpath ($_SERVER['SCRIPT_FILENAME']) == realpath (__FILE__)) {
die ( 'to err is human, human!' ); }
/*
corz.org css-init
dynamic style sheet initialisation v1.9.4
The cleverest script you are reading right now!
Seriously, I wouldn't dream of doing CSS without running it through this!
For how-to's, notes and all that, see the accompanying text files, usually
named "about this folder.txt" in the modules and snippets directories.
Also here..
<https://corz.org/engine?source=menu§ion=php/corz function library/css-init>
NOTE: If your stylesheet is *not* freely released under the creative commons
license, you will probably want to delete that bit!
;o)
(c) 2004->tomorrow! cor + corz.org ;o)
*/
// I set these elsewhere, so you might want to do it here or there..
// Cache Styles?
// A global preference which user/you can override (see notes below)..
//$site_config['cache_styles'] = false;
// CSS modules + snippets paths NOTE: YOU MUST SET THIS!
//
// Places to look for modules and snippets, as many as you like.
// They will be searched in order and the first match used.
//
// Please specify the /FULL/ path. Note: YOU MUST SET THIS!
//
$site_config['css_modules_paths'] = array();
//$site_config['css_modules_paths'][] = $site_config['full_admin_path'].'/css/modules';
//$site_config['css_modules_paths'][] = $site_config['full_admin_path'].'/css/snippets';
//$site_config['css_modules_paths'][] = $site_config['root'].'/blog/inc/themes/modules';
//*> grab color prefs, etc.
require_once $_SERVER['DOCUMENT_ROOT'].'/inc/init.php';
// everything has a name..
if (!isset($stylesheet)) {
$stylesheet = substr(basename($_SERVER['PHP_SELF']), 0, strrpos(basename($_SERVER['PHP_SELF']), "."));
}
//*> user-override minification..
if (isset($_REQUEST['deminify'])) {
$GLOBALS['site_config']['minify_css'] = false;
$dms = '[de-minified output]';
} else {
header('X-Content-Type-Options: ?deminify to see a de-minified version.');
$dms = ''; // kinda redundant, think about it!
}
//*> send correct headers..
if ($site_config['cache_styles']) {
// use the one in your cache (NOT proxy caches, though)..
session_cache_limiter('private');
header('Content-type: text/css;X-Content-Type-Options: nosniff', true);
header('Cache-control: private, max-age=3600, pre-check=3600', true);
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time() + 3600) . ' GMT', true);
header('Pragma: cache', true);
// NOTE: If you are enabling users to cache style sheets, it is wise to tag
// ?something=<scheme-name> onto the CSS request, so they still get a fresh
// style sheet on a scheme switch.
} else {
// Or grab it fresh, it's only a few Kilobytes..
// I'll wager there's more substance in your CSS than GB's of Twittering!
header('Content-type: text/css');
header('Cache-control: no-store, no-cache, must-revalidate, max-age=0');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 60) . ' GMT');
}
//*> open a buffer..
ob_start();
//*> stylesheet begins..
// if minification is disabled, users can *see* this..
echo '/*
Generated "',$site_scheme['name'],'" adaptive stylesheet for '.$_SERVER['HTTP_HOST'].'
[',$stylesheet,' styles]',$dms,'
This stylesheet is freely released under the creative commons license:
<http://creativecommons.org/licenses/by-nc-sa/1.0/>
*/';
//*> back to the style sheet..
return true;
// note: the configuration file for the various elements is..
// /blog/inc/themes/<your-theme>/<your-theme>.ini
// To edit styles, use http://your-site/blog/inc/scheme-edit.php
function output_CSS() {
//*> grab buffer into string
// wipe buffer, transform string, spit it out..
$stylesheet = ob_get_contents();
ob_end_clean();
$stylesheet = preg_replace_callback("/\@@([^@]+)@@/i", "include_css_module", $stylesheet);
$stylesheet = process_user_css($stylesheet);
//*> output minified, compressed style sheet..
if ($GLOBALS['site_config']['minify_css'] == true) {
$using_gzhandler = false;
// I usually have this set site-wide to 16386..
if (!ini_set('zlib.output_compression', 4096)) { // it's good discipline to get your CSS fit into this! heh
$using_gzhandler = ob_start("ob_gzhandler");
}
ob_start('minify');
echo $stylesheet;
ob_end_flush();
if ($using_gzhandler) { ob_end_flush(); }
} else {
echo $stylesheet;
}
//if (!empty($GLOBALS['do_debug']) and $GLOBALS['do_debug'] > 0) { debug("out"); }//:debug:
}
// modules within modules..
function include_css_module($matches) {
ob_start();
$snippet = false;
$module = $matches[1];
//*> process sent values..
// set new values, override existing values..
if (substr($module, -1, 1) == ')') {
$val_pos = strrpos($module, '(', -1);
$sent_vals = substr($module, $val_pos+1, -1);
foreach (explode(',', $sent_vals) as $sent_vals) {
$ev_arr = explode('=', $sent_vals);
// assign value (possibly %%token%%) to site_scheme array, for use in sheet..
$GLOBALS['site_scheme'][$ev_arr[0]] = $ev_arr[1];
}
$module = substr($module, 0, $val_pos);
}
if (substr(strrchr($module, "."), 1) != 'css') {
$module .= '.css';
}
//*> include modules..
foreach ($GLOBALS['site_config']['css_modules_paths'] as $modules_path) {
if (file_exists($modules_path.'/'.$module)) {
if (stristr($modules_path, 'snippet')) {
include $modules_path.'/'.$module;
$snippet=true;
} else {
include_once $modules_path.'/'.$module;
}
break;
}
}
$inc_module = ob_get_contents();
ob_end_clean();
//*> module sub-include..
if (!$snippet) {
$inc_module = preg_replace_callback("/\@@([^@]+)@@/i", "include_css_module", $inc_module);
}
//*> process tokens..
$inc_module = process_user_css($inc_module);
return $inc_module;
}
//*> Process CSS, pluck out %%tokens%%..
//
// transform into real scheme values (PCRE look-ahead for % values)..
function process_user_css($user_stylesheet) {
$user_stylesheet = preg_replace_callback("/%%(((?!%%).)*)%%/i", "switch_scheme_tokens", $user_stylesheet);
return $user_stylesheet;
}
//*> The magical %%token%% switching..
//
function switch_scheme_tokens($matches) {
if (substr($matches[1], -1, 1) !== ')') {
if (isset($GLOBALS['site_scheme'][$matches[1]]) ) {
return $GLOBALS['site_scheme'][$matches[1]];
} else {
return ''; // plain empty value
}
} else { // Math in your CSS!
// pluck expression out of braces..
$val_pos = strrpos($matches[1], '(', -1);
$sent_val = substr($matches[1], $val_pos+1, -1); // +4 / +4.2 / +.2 / -.5 / etc.
$operator = $sent_val{0}; // + / - / etc.
$sent_val = substr($sent_val, 1); // 4 / 4.2 / .2 / .5 / etc.
// percentage calculation..
if (substr($sent_val, -1) == '%') { // 10%
$math_mode = '%'; // %
$sent_val = substr($sent_val, 0, -1); // 10
}
// get original property and value..
$orig_val = $GLOBALS['site_scheme'][substr($matches[1], 0, $val_pos)]; // $site_scheme["top_space"] == 5rem
preg_match_all('/(\d|\.)|(.+)/', $orig_val, $matches);
$orig_val = implode($matches[1]); // 5
$orig_unit = implode($matches[2]); // rem
// do percentage..
if (isset($math_mode)) {
$sent_val = ($orig_val / 100) * $sent_val;
}
switch ($operator) {
case '+': $new_val = $orig_val + $sent_val; break;
case '-': $new_val = $orig_val - $sent_val; break;
case '*': $new_val = $orig_val * $sent_val; break;
case '/': $new_val = $orig_val / $sent_val; break;
default : $new_val = $orig_val;
}
return round($new_val, 3).$orig_unit;
}
}
//*> compress CSS
// remove all white-space and comments..
// I think this code originally came from the php manual.
function minify($css) {
$css = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $css);
$css = str_replace(array("\r\n", "\r", "\n", "\t", ' ', ' ', ' '), '', $css);
return $css;
}
?>