W ostatnich czasie coraz więcej mówi się o nadchodzącym CSS3, który udostępnia web-developerom wiele nowych właściwości i funkcjonalności.
Niestety większość silników przeglądarek wprowadza swoje własne przedrostki dla tych własności, oznaczając je w ten sposób jako eksperymentalne. Mowa tutaj o -webkit-, -o-, -moz- czy -html-. W niniejszym wpisie opiszę sposób skracający pracę z tymi własnościami CSS3.
Kilka słów o vendor-prefixed
Vendor-prefixed to właśnie wspomniane przedrostki w eksperymentalnych własnościach CSS3. Zostają one dodane do takich wynalazków jak np. :
- border-radius
- transform
- box-shadow
- text-shadow
- transition
- text-stroke
- text-fill-color
- text-overflow
- appearance
I własności tych z pewnością przybędzie, więc powyższa lista nie jest pełna. Przykładowo, aby dodać cień do tekstu należy wklepać następujący kod CSS:
1 2 3 4 5 | -moz-text-shadow: 2px 2px 3px black; -webkit-text-shadow: 2px 2px 3px black; -o-text-shadow: 2px 2px 3px black; -khtml-text-shadow: 2px 2px 3px black; text-shadow: 2px 2px 3px black; |
Prawda, że nie jest to zbyt efektywne? Niemniej jednak zadziała dla silników takich jak: WebKit, Opera, Mozilla, Konqueror. Ponadto własność ta musi zostać dodana także bez przedrostka, o czym możemy przeczytać tutaj.
Powyższy kod z różnymi przedrostkami jest również poprawny z punktu widzenia walidacji CSS3, więc nie musimy się tym martwić. Poza tym dostępnych jest więcej prefiksów dla różnych silników przeglądarek, dlatego polecam zapoznać się z całą listą vendor keywords (w powyższym przykładzie znajdują się tylko wybrane silniki, które wprowadzają własności CSS3).
Co jeśli musimy zmienić kolor cieniowania dla naszego tekstu? Niestety wiąże się to z koniecznością edycji pięciu własności, zamiast jednej. Postarajmy się więc temu zapobiec.
Style CSS generowane w PHP
Możemy zrobić następującą rzecz. Do naszego dokumentu dołączamy standardowy element LINK z następującym adresem do stylów CSS:
1 | <link rel="stylesheet" type="text/css" href="style.php" media="all" /> |
Jak widać, odsyłamy przeglądarkę do pliku style.php, zamiast do dokumentu CSS. Niemniej jednak jest to prawidłowe i możemy tak postąpić (można również przy pomocy mod_rewrite przepisać adres, tak by wyglądał na style.css lub można również dołączać kod PHP bezpośrednio do CSS i pliki o rozszerzeniu CSS dodać do wykonywanych plików przez parser PHP).
Nasz przykładowy plik style.php może teraz wyglądać następująco:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php header('Content-Type: text/css; charset=UTF-8'); ?> @charset "utf-8"; h1 { margin: 0; padding: 0; font-weight: normal; color: #32639A; font-family: Georgia, "Times New Roman", Times, serif; } |
Co tutaj się dzieje? Najpierw otwieramy znacznik PHP i powiadamiamy przeglądarkę w nagłówku, że ma do czynienia z plikiem CSS (kodowanie znaków w UTF-8).
Potem możemy już dodawać normalne style CSS, jak powyżej dla elementu H1.
W powyższym dokumencie nie ma jak na razie niczego ciekawego i rozwiązującego nasze problemy. Czas więc się za to zabrać.
Generowanie własności z prefiksami
Aby skrócić sobie zabawę z wypisywaniem własności z vendor-prefixed dla każdego silnika przeglądarek z osobna, dopiszemy prostą funkcję, która tym się za nas zajmie.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /** * Add vendor-prefixed CSS3 * * @param string property name * @param string property value * * @return string property with vendor-prefixed CSS3 **/ function _exp($property, $value) { $css = null; // mozilla $css .= "-moz-$property: $value;\n"; // webkit $css .= "-webkit-$property: $value;\n"; // opera $css .= "-o-$property: $value;\n"; // konquero $css .= "-khtml-$property: $value;\n"; // non-vendor-prefixed CSS3 $css .= "$property: $value;\n"; return $css; } |
A później wystarczy tylko:
1 | _exp('text-shadow', '2px 2px 3px black'); |
Czyli podsumowując, przykładowy dokument CSS może wyglądać następująco:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <?php header('Content-Type: text/css; charset=UTF-8'); /** * Add vendor-prefixed CSS3 * * @param string property name * @param string property value * * @return string property with vendor-prefixed CSS3 **/ function _exp($property, $value) { $css = null; // mozilla $css .= "-moz-$property: $value;\n"; // webkit $css .= "-webkit-$property: $value;\n"; // opera $css .= "-o-$property: $value;\n"; // konquero $css .= "-khtml-$property: $value;\n"; // non-vendor-prefixed CSS3 $css .= "$property: $value;\n"; return $css; } ?> @charset "utf-8"; h1 { margin: 0; padding: 0; font-weight: normal; color: #32639A; font-family: Georgia, "Times New Roman", Times, serif; <?= _exp('text-shadow', '2px 2px 3px black'); ?> } |
Natomiast po odpaleniu w przeglądarce zostanie wypluty poniższy kod CSS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @charset "utf-8"; h1 { margin: 0; padding: 0; font-weight: normal; color: #32639A; font-family: Georgia, "Times New Roman", Times, serif; -moz-text-shadow: 2px 2px 3px black; -webkit-text-shadow: 2px 2px 3px black; -o-text-shadow: 2px 2px 3px black; -khtml-text-shadow: 2px 2px 3px black; text-shadow: 2px 2px 3px black; } |
Wydaje mi się to bardzo użytecznym rozwiązaniem upraszczającym sporo niepotrzebnej roboty.
Epilog
Oczywiście do powyższego dokumentu CSS warto by dodać:
- buforowanie wyniku, tak aby tylko raz wykonywać po stronie serwera powyższe polecenia PHP i odciążyć tym samym serwer z niepotrzebnych operacji,
- przepisywanie linków na „przyjazne”, dzięki czemu nikt nie będzie krzywił miny na dokument CSS zapisany pod rozszerzeniem PHP,
- można również dodać wykrywanie przeglądarki i serwować tylko prefiks dla odpalonego silnika – jednak nie wiem czy byłoby to opłacalne z punktu widzenia optymalizacji i należałoby uprzednio przetestować ten sposób.
Na zakończenie prosty przykład z buforowaniem:
style.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | header('Content-Type: text/css; charset=UTF-8'); // Include Cache_Lite from PEAR require_once "Cache/Lite.php"; /** * Add vendor-prefixed CSS3 * * @param string property name * @param string property value * * @return string property with vendor-prefixed CSS3 **/ function _exp($property, $value) { $css = null; // mozilla $css .= "-moz-$property: $value;\n"; // webkit $css .= "-webkit-$property: $value;\n"; // opera $css .= "-o-$property: $value;\n"; // konquero $css .= "-khtml-$property: $value;\n"; // non-vendor-prefixed CSS3 $css .= "$property: $value;\n"; return $css; } // save css in cache $options = array( 'cacheDir' => 'temp/', 'lifeTime' => 0 ); $cache = new Cache_Lite($options); if ($data = $cache->get('css_style')) { echo $data; } else { ob_start(); include 'style.css'; $data = ob_get_contents(); $cache->save($data); } |
style.css
1 2 3 4 5 6 7 8 9 10 | @charset "utf-8"; h1 { margin: 0; padding: 0; font-weight: normal; color: #32639A; font-family: Georgia, "Times New Roman", Times, serif; <?= _exp('text-shadow', '2px 2px 3px black'); ?> } |
Mam nadzieję, że powyższa technika komuś się przyda. Sam chyba zacznę z niej korzystać przy kolejnych projektach (w połączeniu z CSS Minify).
Vendor prefixed może być całkiem istotnym postępem przeciwko dopisywaniu ciągle tych samych fragmentów tekstu – myślę, że to jedna z najistotniejszych informacji jakie wyniosłem z tego wpisu. ;]
Wszystko pięknie ładnie – ale jest jeden zonk:
2
3
-webkit-border-radius-bottomleft: 5px;
-moz-border-radius-bottomleft: 5px;
I tutaj cały misterny skrypt padł.
@Michał: faktycznie, nie pomyślałem o tym border-radius (zawsze tylko bezmyślnie kopiuję kod dla tej własności z wykorzystaniem border-radius.com).
Niemniej jednak wystarczy drobna poprawka w powyższej funkcji uwzględniająca ten wyjątek i po sprawie :-)
W świecie railsów furorę robią takie narzędzia jak LESS albo SASS w których to o czym piszesz – i wiele więcej – już istnieje.
Tak jak wyżej zauważył kolega, nie wszystkie właściwości CSS3 są pisane jednakowo we wszystkich obsługujących je przeglądarkach. Po drugie, opisywany przez ciebie „text-shadow” akurat w ogóle nie potrzebuje prefiksu, ponieważ już od dawna jest obsługiwany po prostu jako „text-shadow” w Operze, Firefoksie, Chrome i Safari.
Mimo to wpis ciekawy.
Macie racje Panowie, powyższy skrypt nie jest idealny. Żeby wszystkie te nowe własności obsługiwać w prawidłowy sposób należałoby napracować się znacznie bardziej, ja podałem tylko skrócone rozwiązanie.
1) Można by dodać własności, które wymagają przedrostków.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
$allow_property = array(
'animation', 'animation-delay', 'animation-direction', 'animation-duration', 'animation-iteration-count', 'animation-name', 'animation-play-state', 'animation-timing-function',
'appearance',
'backface-visibility',
'baseline-shift',
'binding',
'bookmark-label', 'bookmark-level', 'bookmark-target',
'background-break', 'background-clip', 'background-origin', 'background-size',
'border-image', 'border-image-outset', 'border-image-repeat', 'border-image-slice', 'border-image-source', 'border-image-width', 'border-length',
'border-radius', 'border-bottom-left-radius', 'border-bottom-right-radius', 'border-top-left-radius', 'border-top-right-radius',
'box-align', 'box-decoration-break', 'box-direction', 'box-flex', 'box-flex-group', 'box-lines', 'box-ordinal-group', 'box-orient', 'box-pack', 'box-shadow', 'box-sizing',
'color-profile',
'column-break-after', 'column-break-before', 'column-count', 'column-fill', 'column-gap', 'column-rule', 'column-rule-color', 'column-rule-style', 'column-rule-width', 'column-span', 'column-width', 'columns',
'crop',
'display-model', 'display-role',
'dominant-baseline',
'drop-initial-after-adjust', 'drop-initial-after-align', 'drop-initial-before-adjust', 'drop-initial-before-align', 'drop-initial-size', 'drop-initial-value',
'fit', 'fit-position',
'float-offset',
'font-size-adjust', 'font-stretch',
'grid-columns', 'grid-rows',
'hanging-punctuation',
'hyphenate-after', 'hyphenate-before', 'hyphenate-character', 'hyphenate-lines', 'hyphenate-resource',
'hyphens',
'icon',
'image-orientation', 'image-resolution',
'inline-box-align',
'line-stacking', 'line-stacking-ruby', 'line-stacking-shift', 'line-stacking-strategy',
'mark', 'mark-after', 'mark-before', 'marks',
'marquee-direction', 'marquee-play-count', 'marquee-speed', 'marquee-style',
'move-to',
'nav-down', 'nav-index', 'nav-left', 'nav-right', 'nav-up',
'outline-offset',
'page-policy',
'perspective', 'perspective-origin',
'phonemes',
'presentation-level',
'punctuation-trim',
'rendering-intent',
'resize',
'rest', 'rest-after', 'rest-before',
'rotation', 'rotation-point',
'ruby-align', 'ruby-overhang', 'ruby-position', 'ruby-span',
'string-set',
'tab-side',
'target', 'target-name', 'target-new', 'target-position',
'text-emphasis', 'text-justify', 'text-outline', 'text-replace',
'transform', 'transform-origin', 'transform-style',
'transition', 'transition-delay', 'transition-duration', 'transition-property', 'transition-timing-function',
'voice-balance', 'voice-duration', 'voice-pitch', 'voice-pitch-range', 'voice-rate', 'voice-stress', 'voice-volume',
'white-space-collapse'
);
if (!in_array($property, $allow_property)) {
return "$property: $value;\n";
}
:D
2) Należałoby dodać obsługę wyjątków w pisowni niektórych własności, przykładowo:
2
3
4
5
6
7
8
9
10
11
12
13
14
case 'border-bottom-left-radius':
case 'border-bottom-right-radius':
case 'border-top-left-radius':
case 'border-top-right-radius':
$css .= _borderXradius($property, $value);
break;
default:
$css .= addVendorPrefixed($property, $value);
break;
}
Kod musiałby być dużo bardziej złożony i na bieżąco aktualizowany, bowiem przeglądarki wciąż implementują kolejne własności i funkcjonalności z CSS3.
Dużo roboty, a jeszcze więcej trudności z samodzielnym zapamiętaniem by samemu stosować w praktyce. Trochę nieciekawie to wygląda – kolejne komplikacje, jakby nie można było tego zrobić normalnie i zgodnie ze specyfikacją.
Stąd próba usystematyzowania i ułatwienia z mojej strony, choć niekoniecznie udana – trza by dokończyć robotę na porządnie. Poza tym sam kod pewnie rozrastałby się w dość szybkim tempie, więc dobrze by było przemyśleć zastosowaną architekturę rozwiązania na nowo – powyższy sposób nie jest zbyt piękny :-)
@Alan: dopiero teraz czytam Twój komentarz, ponieważ już po raz drugi trafiłeś do spamu i nie dostałem powiadomienia o nowym komentarzu – nawet nie wiem dlaczego tak się dzieje, ale to tylko przy komentarzach od Ciebie.
Co do LESS czy innych tego typu, słyszałem i całkiem fajna sprawa. Nie sądziłem jednak, że mają już zaimplementowaną i tą funkcjonalność, o której pisałem w tym wpisie – dzięki za informację! :-)
https://github.com/codler/jQuery-Css3-Finalize
a tutaj wtyczka dla jQuery, dzięki której możemy w skrótowy sposób używać nowych własności CSS3 w jQuery :-)
Ciekawostka, nowa strona główna Interii już używa css3, oraz warunkowych stylów dla IE.
Warunkowe style są passe :)
http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/
@zzz: nie tylko Interia, ale większość nowych stron, w tym w niewielkim stopniu nawet Google (HTML5 & CSS3).
Także i ja w większości swoich projektów używam HTML3 & CSS3, najczęściej w ogóle nie zważając na IE6. W końcu to od nas – webdeveloperów – zależy kiedy stare i nieaktualne przeglądarki znikną z rynku i tym samym ułatwimy sobie życie.