Liz Castro <lcastro@cookwood.com> wrote:

> This is a simplified form of Tantek Celik's box model hack, right?

Yep, that's the plan. It seems to work just as well, but it hasn't been
nearly as widely implemented as the original, so there may be kinks I've
overlooked somewhere.

> I see that [Tantek's] works, but I don't quite understand why...

CSS has its own 'escaping' scheme to allow out-of-bound characters to
be included in strings and identifiers. For example, <div id="foo.bar"> is
valid, but to style it you wouldn't be able to say 'div#foo.bar', as the
dot is taken as being part of a class selector. Instead you have to write
'div#foo\.bar'. The backslash removes any special meaning from the character
after it. Any character can be escaped this way, even characters that do
not need to be escaped, like '\w'. (However, '0' to '9' and 'a' to 'f' have
a different interpretation, they are taken as hex charcter codes.)

The important point is that some browsers do not understand CSS escapes,
and it is possible to exploit their misinterpretation of what a backslash
means to present them with different styles. The BMH aims to give a different
'width' property to IE5/Win, which both (a) doesn't understand CSS escapes,
and (b) misinterprets the 'width' property.

The BMH
=======

  div.content {
    width: 400px;
    voice-family: "\"}\"";

The above line is interpreted by a correct CSS parser as being the same as:

    voice-family: '"}"';

which is an odd name for a voice-family but not an invalid one. It then goes
on to the next property:

    voice-family: inherit;

which just sets it back to what it would have been before. Any property that
accepts a quoted value could be used to do this, there is nothing special
about voice-family. Anyway, the correct parser then sees the final property:

    width: 300px;
  }

which makes the div thinner to make room for its padding.

On the other hand, a browser whose implementors never heard of CSS escapes
will read:

  div.content {
    width:400px;
    voice-family: "\"}\"";

and this time the line above will be interpreted as if the blackslashes were
not special characters at all:

    voice-family: "/"
  }
  /"";

so it'll set the voice-family to a backslash(*) and end the rule
immediately, leaving the wider width setting in place. It then comes across
some quotes, a semicolon, 'voice-family: inherit;', 'width: 300px;' and a
'}', but since these occur outside a rule and aren't valid selectors
they'll probably be ignored, until the start of the next rule.

(* this is the reason 'voice-family' was chosen for the hack - the
older graphical browsers we are targeting with it don't support Aural CSS
anyway, so it doesn't matter if the voice gets left with a weird value.)

Being nice to Opera
===================

One kink in this strategy is that there are browsers (Opera, before version 6,
and I think some others) that get the box model right, but still don't
understand CSS escapes. For this reason, the above rule tends to have this
one somewhere after it:

  body>div.content {
    width: 300px;
  }

which gives the correct width to browsers that understand the child
selector '>' as well as those that support escapes. IE understands neither,
so it keeps its 'wrong' width to compensate for its bugs.

Simplified BMH
==============

The SBMH seeks to reduce the ugliness a little by applying CSS escapes to
the property name instead of the value of a property like 'voice-family'.
It is unusual to use CSS escapes in property names because no property
name has any characters in it that need escaping, but it's perfectly valid.

  div.content {  width: 400px; } /* wrong value */
  div.content { w\idth: 300px; } /* for standards browsers */

Browsers that do not understand escapes will see the second property name
as something weird with a symbol in it, and will ignore it. It fact they
are likely to ignore the entire rule. They will stick with the wider
setting in the previous rule.

Browsers that do understand it see 'w\idth' as being exactly the same as
'width', and override the first version with the narrower, correct one.

This suffers from the same problem as the original BMH regarding browsers
that get the box model right but don't support escapes. The SBMH gets
around this by noting that although IE5/Win doesn't support escapes, it
will happily ignore any backslashes that occur before the start of a token.
Other browsers that don't support escapes won't allow this. So we add a
third rule:

  div.content {  width: 300px; } /* for browsers with no escapes at all */
  div.content { \width: 400px; } /* for IE5/Win */
  div.content { w\idth: 300px; } /* for good browsers */

simplistic browsers understand only the first rule. Standards browsers
understand them all, so use the third. Stuck in the middle is IE, whom
we catch as serve a different style.

One can do the same thing with other properties too, eg.

   height
  \height
  he\ight

'h\eight' is no good as the 'e' is taken as part of a hexadecimal escape.
Indeed the only properties one can't use the this to hack for IE5/Win are
those than begin with letters 'a'-'f'.

Caveats
=======

This applies to both the original BMH and the SBMH. First, you must use
a Standards-mode DOCTYPE in your HTML. IE6 supports CSS escapes properly,
but it will only use the correct box model in Standards. In Quirks mode it
would read the 'correct' value from the CSS but use it in the same 'wrong'
way as IE5 did.

Secondly, using CSS escapes at all with confuse Netscape 4 to the point of
making it ignore the *entire* stylesheet. Other stylesheets linked to the
page are still used OK. If you are withholding styles from N4, or using
a separate stylesheet, that's obviously not an issue. But if N4
compatibility is required, it's better to use the alternative workaround
of simply not specifying an element with width and non-zero padding at
the same time. The same effect can usually be achieved by putting the
padding on a second div inside the one with set width anyway.

Erm, does that answer your question? What was it again? ;-)
and@doxdesk.com