CSS-curiositeiten uit het verleden

CSS is de manier waarop we zaken op het web stylen, maar sinds het begin is het af en toe gevraagd om meer rollen op zich te nemen. Het heeft vreemde functies op zich genomen en gedragingen overgenomen die het waarschijnlijk niet had moeten hebben. Zo gaat dat nu eenmaal.

Net zoals ik eerder vreemde en contextspecifieke HTML heb beschreven, is dit een blik op CSS, grotendeels buiten de officiële specificaties. Browser-specifieke hacks, syntax beperkt tot bepaalde technologieën en snippets exclusief voor bepaalde engines, gesmeed uit twijfelachtige omstandigheden, bedrijfscomplicaties en esoterische implementaties.

Property Parsing

width: 300px;
*width: 250px;
_width: 200px;
-width: 200px;

De meeste browsers zouden, terecht, een eigenschap die begint met een asterisk als ongeldig beschouwen. Internet Explorer 7 en oudere versies behandelden dit echter als geldig. Deze methode werd zo bekend dat het de naam 'star hack' kreeg, naar de vorm van de asterisk.

Hetzelfde gold voor het voorafgaan van een eigenschap met een underscore of een koppelteken; alleen Internet Explorer 6 beschouwde dit als geldig. Er waren veel meer soortgelijke hacks in gebruik, waarvan de overgrote meerderheid het best is gedocumenteerd op de gelijknamige website browserhacks.com. De kern is dat sommige browsers eigenschappen, selectors en waarden onjuist parseerden, wat in een tijdperk waarin browsergedrag sterk varieerde, in het voordeel van de ontwikkelaar kon worden gebruikt.

Het beperken van CSS tot bepaalde browsers via conditionele commentaren in CSS-bestanden — iets wat wel kon in HTML-documenten — was niet mogelijk.[^1] Daarom was dit misbruik van de twijfelachtige parsing van wat wel en niet geldig was, gebruikelijk om specifieke browsers te targeten.

Important

background: red !interesting;

Internet Explorer 7 en oudere versies behandelden bijna elke tekststring die begon met een uitroepteken als !important. Meestal maakten mensen hier gebruik van door !ie te schrijven, zodat een stijl de specificiteit alleen in Internet Explorer zou overschrijven. Voor zover Internet Explorer het betrof, waren de willekeurige !banana en de gespecificeerde !important hetzelfde, terwijl andere browsers correct alleen die laatste accepteerden.

Er was een andere gerelateerde bug in Internet Explorer 6 en oudere versies, waarbij een stijl die later in hetzelfde blok werd gedeclareerd, een !important-waarde zou overschrijven. In het volgende voorbeeld zou de kleur zwart zijn in plaats van wit, zoals het zou moeten zijn:

color: white !important;
color: black;

Browserdetectie op documentniveau

Er waren talloze manieren om op documentniveau via CSS te identificeren welke browser werd gebruikt. Enkele voorbeelden:

  • In Internet Explorer 6 en oudere versies kon men * html {} schrijven.
  • Alleen voor Internet Explorer 7 kon men *:first-child+html {} gebruiken.
  • Browsers anders dan Internet Explorer 7 ondersteunen html > /**/ body {}.
  • Vroege versies van Firefox pasten stijlen toe binnen body:empty {} (zelfs wanneer de body inhoud had).

Deze aanpak was vaak rommelig in gebruik, omdat het betekende dat er meerdere afdalende selectors geschreven moesten worden om elementen in specifieke browsers te targeten.

Clearfix

.clearfix {
  zoom: 1;
}

Tot versie 8 had Internet Explorer een concept genaamd hasLayout. Dit was een interne vlag die aangaf of een element verantwoordelijk was voor het renderen van zichzelf (true) of dat een ouder-element hiervoor verantwoordelijk was (false). Natuurlijk was dit een ondoorzichtig en verwarrend systeem. Sommige elementen, zoals <img>, <iframe> en de meeste input-gerelateerde zaken, hadden intrinsiek 'layout'. Andere elementen hadden geen layout, tenzij dit specifiek werd toegekend via bepaalde CSS-declaraties.

Een element zonder layout kon last krijgen van vreemd gedrag bij marges en borders, complicaties bij positionering en algemene weergavefouten. Het belangrijkste was dat het ervoor zorgde dat ouder-containers inklapten wanneer hun kinderen 'gefloat' waren. Dit veroorzaakte allerlei problemen in een tijd waarin het floaten van elementen de de facto manier was om pagina's te lay-outten.

De zoom-eigenschap verandert de grootte waarin het doel verschijnt. Met een waarde van 1 verschijnt het doel in zijn bestaande grootte. Dit lijkt nutteloos, maar zoom geeft een element layout. Daarom werd het gebruikt als een eenvoudige manier om layout te forceren zonder het visuele uiterlijk van een element te veranderen.

Hand-cursor

cursor: pointer;
cursor: hand;

Vóór Internet Explorer 6 respecteerde Internet Explorer de waarde pointer voor de cursor-eigenschap niet. In plaats daarvan ondersteunde het alleen de niet-standaard waarde hand, die een wijzende hand weergave. Daarom schreven websites gewoonlijk beide waarden in hun CSS, in de verwachting dat cursor: hand als ongeldig zou worden beschouwd in andere browsers dan Internet Explorer (waardoor zij pointer zouden gebruiken), terwijl Internet Explorer hand zou gebruiken.

CSS-expressies

top: expression(eval(document.documentElement.scrollTop));

CSS-expressies, formeel 'Dynamic Properties' genoemd, waren een manier om JavaScript in CSS uit te voeren. Dit was niet-standaard gedrag dat werd geïntroduceerd in Internet Explorer 5 om tekortkomingen in de stylingmogelijkheden op te vangen, zoals het ontbreken van ondersteuning voor position: fixed (waar de bovenstaande snippet naar verwijst), of min-width en max-width.

Dit maakte het maken van layouts lastig. CSS-expressies waren onhandig en complex. Men kon zelfs gaan zo ver om te bepalen welke stijlen werden toegepast op basis van het tijdstip van de dag met behulp van Date(). CSS-expressies werden constant opnieuw geëvalueerd, wat ze extreem zwaar maakte voor de prestaties. Zoals Steve Souders op de Yahoo! Developer Network blog opmerkte:

"Het probleem met expressies is dat ze vaker worden geëvalueerd dan de meeste mensen verwachten. Ze worden niet alleen geëvalueerd wanneer de pagina wordt gerenderd en geresized, maar ook wanneer de pagina wordt gescrold en zelfs wanneer de gebruiker de muis over de pagina beweegt."

Microsoft beëindigde de ondersteuning hiervoor bij Internet Explorer 8, wat werd aangekondigd in hun dramatisch getitelde post 'Ending Expressions'.

Filters

Internet Explorer vertoonde een aantal onbetrouwbare gedragingen en liep achter op andere browsers wat betreft visuele effecten, zelfs voor basiseigenschappen zoals opacity. Daarom introduceerde Microsoft eigen filterfunctionaliteit. Filters maakten gebruik van DirectX-gebaseerde componenten van Windows, ver verwijderd van de webplatformstandaarden. Internet Explorer introduceerde deze functionaliteit voor het eerst in versie 4. De syntax was vrij eenvoudig; een voorbeeld van het verlagen van de opaciteit van een element zag er zo uit:

filter: alpha(opacity=50);

Het was verre van perfect, maar het maakte het mogelijk om veel tekortkomingen van Explorer aan te pakken. Filters vereisten dat een element 'layout' had en stripte bovendien irritant genoeg de ClearType font anti-aliasing voor kleine tekst, waardoor deze grillig oogde. Soms veroorzaakten ze ook problemen met interactiviteit. In versie 5.5 werd de filter-syntax complexer:

filter: progid:DXImageTransform.Microsoft.Alpha(opacity=50);

Let hierbij op de aanwezigheid van 'DX', wat verwijst naar DirectX. Filters konden zelfs geanimeerd worden. In de laatste update van de filterfunctionaliteit voegde Microsoft in versie 8 een vendor-prefix toe aan hun filter-eigenschap om beter te voldoen aan de CSS-standaarden. Ze zorgden er ook voor dat filters de font anti-aliasing niet meer zouden strippen, en het werd nodig om de waarde tussen aanhalingstekens te zetten:

-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(opacity=50)";

-ms-filter werd uiteindelijk verwijderd in Internet Explorer 10. Deze verwijdering gold ook voor de legacy-modi van Internet Explorer. Tegen de release van versie 10 hadden de meeste populaire toepassingen van filters ondersteuning gekregen van de browser, zoals de opacity-eigenschap die in versie 9 werd toegevoegd.

Naast opaciteit waren twee andere zeer gangbare toepassingen van de filtermogelijkheden van Explorer het afhandelen van transparante PNG-afbeeldingen en het toepassen van gradiënten. Transparante secties van PNG's in Internet Explorer 6 en oudere versies werden vervangen door een grijze achtergrond. Zelfs in latere versies konden PNG's problemen ondervinden, meestal gerelateerd aan gamut. Daarom laadden mensen afbeeldingen vaak via AlphaImageLoader:

filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='image.png');

Gradiënten werden toegepast met filter als alternatief voor het gebruik van achtergrondafbeeldingen (linear-gradient() zou pas in Internet Explorer versie 10 worden ondersteund):

filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='red', endColorstr='green', GradientType=0);

Mensen implementeerden vaak eenvoudige, herbruikbare filters voor veelvoorkomende situaties met behulp van de eerder genoemde CSS-expressies.

Scrollbalken

body {
  scrollbar-face-color: #333333;
  scrollbar-highlight-color: #666666;
  scrollbar-3dlight-color: #000000;
  scrollbar-darkshadow-color: #000000;
  scrollbar-shadow-color: #111111;
  scrollbar-arrow-color: red;
  scrollbar-track-color: #222222;
}

Internet Explorer versie 5.5 introduceerde de mogelijkheid om het uiterlijk van scrollbalken aan te passen. De bovenstaande stijlen werkten nog in Internet Explorer 11.

Voor de pijlknoppen en de 'thumb' deed face-color de achtergrond, highlight-color de binnenste linker highlight, 3dlight-color de buitenste rechter highlight, darkshadow-color de binnenste rechter schaduw, shadow-color de binnenste rechter schaduw, en arrow-color de pijl-iconen zelf. track-color stelde uiteraard de track van de scrollbalk in. Er waren zelfs volledige applicaties om deze stijlen te genereren.

Net als tegenwoordig werd het stylen van scrollbalken als enigszins schreeuwerig en overdreven beschouwd, dus werd het vooral gebruikt voor kleine aanpassingen in professionele contexten. De functionaliteit werd uiteraard optimaal benut door mensen die hun MySpace-profielen aanpasten.

HTML-componenten

.item {
  behavior: url(csshover.htc);
}

HTML-componenten (niet te verwarren met de hedendaagse Web Components) waren een exclusieve functie van Internet Explorer, toegevoegd in versie 5 en sterk uitgebreid in versie 5.5. Hiermee kon scripting-logica aan HTML-elementen worden gekoppeld via CSS. Hoewel veel talen werden ondersteund, werd bijna altijd JavaScript gebruikt.

Hier is een voorbeeld van een HTML-component die werd gebruikt om 'hack-in' hover-gedrag toe te voegen aan elementen die geen links waren, aangezien :hover in Internet Explorer vóór versie 7 alleen voor links werd ondersteund. Let op dat de opgegeven taal 'JScript' is — Microsofts eigen implementatie van JavaScript die door Internet Explorer werd ondersteund.

<PUBLIC:COMPONENT NAME="CSSHover">
<PUBLIC:ATTACH EVENT="onmouseover" ONEVENT="hoverOn()" />
<PUBLIC:ATTACH EVENT="onmouseout" ONEVENT="hoverOff()" />
<script language="JScript">
// Voeg een class toe wanneer de muis het element betreedt
function hoverOn() {
  if (!/\bhovered\b/.test(element.className)) {
    element.className += " hovered";
  }
}
// Verwijder de class wanneer de muis het element verlaat
function hoverOff() {
  element.className = element.className.replace(/\b\s?hovered\b/g, "");
}
</script>
</PUBLIC:COMPONENT>

Een bijzonder populair gebruik van deze functionaliteit was Progressive Internet Explorer, dat moderne CSS-functies portte naar Internet Explorer versies 6 tot en met 9. Vanaf Internet Explorer 8 is behavior beschikbaar als de vendor-prefixed -ms-behavior, en de ondersteuning werd verwijderd in Internet Explorer 10.

Box Model Hack

div {
  width: 400px;
  voice-family: "\"}\"";
  voice-family: inherit;
  width: 300px;
}

Een zeer beroemde hack van Tantek Çelik. De eigenschap voice-family werd gebruikt om aan te geven welke stemfamilie gebruikt moest worden bij het hardop voorlezen van content. De eigenschap werd niet ondersteund door oudere browsers uit die tijd, en de waarde werd verstrekt als een string. Door een bug in de parser interpreteerde Internet Explorer 5 \"}\" als het sluiten van het huidige CSS-blok, waardoor de overige declaraties ongeldig werden en onopgemerkt door de browser gleden.

Dit was belangrijk, omdat de implementaties van het box model verschilden. De Windows-versies van Internet Explorer die door deze hack werden geraakt — versie 5 en 5.5 — hanteerden het box model onjuist. Zij rekenden padding en borders mee binnen de gedeclareerde breedte, in plaats van daarbuiten zoals het hoort. Ondertussen ondersteunden standaard-conforme browsers die de CSS correct parseerden (met uitzondering van Opera, waarvoor een extra snippet werd toegevoegd) het box model en behandelden zij borders en marges correct als zijnde buiten de breedte van het element.

Holly Hack

/* Verbergt voor IE5 op Mac \*/
* html .element {
  height: 1%;
}
/* Einde verbergen voor IE5 op Mac */

Internet Explorer voor Mac gebruikte een geheel andere engine dan Internet Explorer op andere platforms. Terwijl de meeste platforms Trident/MSHTML gebruikten, gebruikte Explorer voor Mac Tasman, die zijn eigen bugs had. Een van deze bugs was dat commentaren met een escape-karakter vóór het sluiten (\*) verkeerd werden behandeld.

De inhoud vóór het volgende correct gesloten commentaar werd door Internet Explorer voor Mac als een commentaar beschouwd en dus niet geparsed, terwijl andere browsers dit wel correct behandelden. In de bovenstaande snippet wordt een Holly Hack (genoemd naar Holly Bergevin) gecombineerd met een hack voor browserdetectie die gericht is op Internet Explorer 6 en oudere versies. Zo zou een fix voor ongewenst gedrag dat exclusief was voor Internet Explorer op Windows, alleen worden toegepast op Internet Explorer op Windows.

Double Margin Float

div {
  float: left;
  margin-left: 5px;
  display: inline;
}

Internet Explorer zou de bovenstaande marge weergeven als 5px. Echter, zonder de waarde display: inline, zou deze worden gerenderd als 10px — niet 5px. Zoals wordt uitgelegd in het artikel The IE Doubled Float-Margin Bug van Position Is Everything's Explorer Exposed:

"Waarom gebeurt dit? Stel geen zulke domme vragen! Dit is IE, onthoud dat. Conformiteit aan de specificaties is iets om te hopen, niet om te verwachten. Het simpele feit is dat het gebeurt."

Toolbalken

Voordat de Skype Toolbar opt-out meta tag werd geïntroduceerd — en zelfs als fallback daarna — probeerden sommige site-eigenaren de Skype-toolbalk te voorkomen van het tonen van Skype-knoppen op hun pagina direct via CSS:

span.skype_pnh_container {
  display: none !important;
}
span.skype_pnh_print_container {
  display: inline !important;
}

In een zeer vergelijkbaar geval verscheen de Ask Toolbar bovenaan de pagina en brak deze soms de lay-out, wat leidde ertoe dat ontwikkelaars #apn-null-toolbar en #apn-body-style targetten om deze te verwijderen. Veel andere extensies, vaak geïnstalleerd door applicaties zonder dat gebruikers dit echt begrepen, introduceerden soortgelijke problemen die door browsers getarget en verwijderd moesten worden.

Vendor Prefixes

Vendor-prefixes werden voornamelijk gebruikt voor niet-standaard of experimentele CSS-functies, zodat ontwikkelaars nieuwe functies konden testen en feedback konden geven aan browserleveranciers voordat hun implementatie of specificatie definitief werd. 'Prefix' verwijst naar het browser-specifieke teken dat voorafgaat aan de eigenschap, pseudo-element, at-rule of media-feature.

  • -webkit- werd gebruikt voor WebKit- en Blink-gebaseerde browsers.
  • -moz- voor Gecko-gebaseerde browsers.
  • -o- voor Presto-gebaseerde browsers.
  • -khtml- voor KHTML (en behouden voor vroege WebKit-versies).
  • -ms- voor MSHTML/Trident-gebaseerde browsers, en anderen.

Deze vendor-prefixes werden echter soms voortijdig overgenomen buiten de experimentele fase. Ontwikkelaars namen ze op in productiesites, waardoor het verwijderen van de ondersteuning voor deze testfuncties de sites zou breken. Hierdoor zagen browserleveranciers zich genoodzaakt de ondersteuning voor deze testimplementaties te behouden.

Als gevolg hiervan gebruiken browserleveranciers geen vendor-prefixes meer, maar plaatsen ze functies achter 'flags' die per individuele browser moeten worden ingeschakeld. De meeste vendor-prefixes zijn overbodig geworden nu er niet-geprefixte implementaties zijn. Sommigen blijven echter bestaan omdat er geen officiële specificaties zijn. De bekendste is waarschijnlijk -webkit-text-stroke, die wordt ondersteund door alle grote browsers en niet alleen door diegenen die WebKit gebruiken.

Dit artikel is niet uitputtend. Dit zijn slechts de meest populaire of opvallende niet-standaard elementen. Er zijn nog veel meer vreemde CSS-curiositeiten uit het verleden die op het web rondzwerven, stof verzamelen en krakend oud worden. Zelfs in 2005 probeerde Microsoft mensen al te laten stoppen met het gebruik van veel van deze hacks. Tenminste (buiten legacy-contexten) hoeven we ons niet langer zorgen te maken over het twijfelachtige gedrag van Internet Explorer en andere browsers uit het steentijdperk.

***

[^1]: Mensen omzeilden deze beperking soms door stijlen te wrappen in conditionele commentaren waar ze werden geïmporteerd, door conditionele commentaren te gebruiken om selectief klassen toe te passen, of door inline-stijlen te wrappen in conditionele commentaren, allemaal binnen hun HTML.