← Password Recovery | Jotting Down →
For the last few years, web developers have been increasinglyrelying on CSS (Cascading Stylesheets) for the aspect of their websites. Using CSS provides the tremendous benefit that the underlying code does not have to other with cosmetic details (or almost not at all) and instead merely outputs an XHTML tree decorated with identifiers and classes. A stylesheet is then used to alter the shape, position and aspect of the XHTML nodes in many different ways. And since the stylesheet is usually not dynamic, it can be downloaded once and for all so that the total weight of every page is smaller.
The approach I will be using here is precisely that: the entire JITBrain website has a single CSS file (to be found in /files/cache/style.css) which is downloaded on the first visit and is then used to decorate every single page. If a page needs specific design, the CSS file can specify rules for that page only by prepending the appropriate body class. For instance, if I wish to change the position of the forms on the login page, I would write quite literally:
body.login-page div.content
{
height: 400px;
}
body.login-page form#login-form table
{
position: absolute;
top: 100px;
left: 100px;
}
body.login-page form#signup-form table
{
position: absolute;
top: 100px;
left: 500px;
}
However, I do not want to actually have a single stylesheet file: it would become too easy to get lost as the file gets longer and longer. Instead, I will use two or three generic stylesheets (one for the general page layout, one for the forms, and so on) and the specific per-page stylesheets. All of these will then be automatically transformed into a CSS file by the files controller. For now, I will keep the generation code within the controller, and will move it away once the controller gets too big:
<?php // controllers/files.php $request = $_SERVER['REQUEST_URI']; if (array_key_exists('URL_PREFIX', $_ENV)) { $prefix = $_ENV['URL_PREFIX']; sscanf($request, "$prefix%s", $request); } // Stylesheet if ($request == '/files/cache/style.css') { // Concatenation ob_start(); $root = dirname(dirname(realpath(__FILE__))); $files = str_replace('/', DIRECTORY_SEPARATOR, "$root/style/*.css"); $css = ''; foreach (glob($files) as $file) $css .= file_get_contents($file); // Minor minification $css = strtr($css, "\n\t\r", ' '); $css = preg_replace('/ +/', ' ', $css); $css = preg_replace('/ *([:;{}]) */', '$1', $css); $css = str_replace(';}', '}', $css); // Cache the result if (!ErrorConfig::DeveloperMode()) { $file = "$root/files/cache/style.css"; $file = str_replace('/', DIRECTORY_SEPARATOR, $file); file_put_contents($file, $css); } // Send to the reader header('Content-type: text/css; charset: UTF-8'); echo $css; exit; } header('HTTP/1.1 404 File Not Found'); Page404View::Render($request);
This controllers determines whether the user is looking for the stylesheet. If that’s the case, it reads the contents of the /style directory and concatenates together all the CSS files it finds there. It then minifies the result (removing unnecessary space, mostly) so that the file is lighter, and sends it to the user. If not in development mode, the file is saved to disk so that it is served automatically from then on, instead of having to re-concatenate it on every request.
As for the general design of the page, I want to include a header that contains the name of the software (in this case, JITBrain) that can be clicked to return home as well as a subtitle (something that describes the purpose of the website in a concise and possibly clever way, such as “Remember things, just in time”), and a footer with copyright information and a link to the project website. I will settle for two colors, blue (dark: #CCE, light: #DDF) and green (dark: #CEC, light: #DFD), with green providing the high-level structure (such as headers and footers) and blue providing the lower-level structure (such as headlines). I enclose everything in a body with a width of 960 pixels (it can be divided evenly into 2, 3, 5, 10, 12, 15, 16, 20, 24, 30, 32…) centered on the screen. Links are bright blue and not underlined (except when hovering). I will be using the FamFamFam silk icons, too, stored in /files/img.
I will not paste the CSS here, but there is a modification applied to the PlainPageView, which now really behaves like the main page template:
<?php // views/plainpage.php class PlainPageView { public static function Render($title, $class, LazyObj $contents) { $title = "JITBrain - " . htmlspecialchars($title); $class = htmlspecialchars($class); $root = DomainConfig::Url('/'); $style = DomainConfig::Url('/files/cache/style.css'); $icon = DomainConfig::Url('/files/img/time.png'); ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title><?=$title?></title> <link rel="stylesheet" href="<?=$style?>"/> <link rel="icon" type="image/png" href="<?=$icon?>"/> </head> <body class="<?=$class?>"> <h1 id="title"><a href="<?=$root?>">JITBrain</a></h1> <h2 id="subtitle">Remember things, just in time</h2> <div class="content"> <?php $contents->execute(); ?> </div> <div id="footer"> JITBrain © 2009 Victor Nicollet — <a href="http://www.nicollet.net/jitbrain" target="_blank"> Learn more</a> </div> </body> </html><?php } }
Additional optimizations I could apply would be to set up Apache so that it serves CSS files compressed (for improved performance) with appropriate caching features (to avoid unnecessary requests), or even to distribute them through a network like Amazon S3. I could also hide them behind a better minification tool, like Minify, which handles comments and actual CSS semantics instead of lexical elimination of characters. All of these, though, are ultimately configuration weaking on your web server, rather than actual modifications to PHP code: it’s mostly about finding the optimal way to distribute a static CSS file on a production system, which is a fairly well-documented issue.
Hi. I'm Victor Nicollet,
0 Responses to “13. Stylesheets”