Rendering Quotes With CSS

The W3C recommends that visual user agents render quotation marks according to the specified language attribute, since many languages adopt different styles for quotations. Yet no user agent will render quotations in a language-specific manner in their default style sheet. Let’s change that.

The correct way

CSS 2.1 includes a property called quotes. The quotes property specifies quotation marks for any number of embedded quotations. It’s a tricky property, so before continuing make sure you read the entire section at W3C and understand how the content property defines what type of marks to produce and the quotes property how they will look.

The HTML

Before getting on with the CSS, let’s start with some basic HTML for testing purposes:

  1. <p><q>Generic quote and <q>another quote</q> inside</q></p>
  2. <p lang="sv"><q>Swedish quote and <q>another quote</q> inside</q></p>
  3. <p lang="da"><q>Danish quote and <q>another quote</q> inside</q></p>
  4. <p><q lang="sv">Swedish quote and <q>another quote</q> inside with the lang attribute set in the parent <code><q></code> tag</q></p>
  5. <blockquote><p>This is a generic paragraph with a <q>quote element</q> inside a blockquote</p></blockquote>
  6. <div lang="sv"><blockquote><p>This is a swedish paragraph with a <q>quote element</q> inside a blockquote</p></blockquote></div>
  7. <div lang="da"><blockquote><p>This is a danish paragraph with a <q>quote element</q> inside a blockquote</p></blockquote></div>

Judging from the results of testing this in different browsers, we can make the following conclusions:

  • IE 6 and 7 simply ignores the quotes, no matter what. Probably due to that they do not understand the :before and :after selectors.
  • Opera, Safari 2 and Safari 3 render default double quotes (0022) in all languages, even for nested or inner quotes.
  • Firefox and other gecko browsers render correct US-EN quotes for all languages, regardless of the lang attribute.

The CSS

W3C recommends the following CSS code to correctly render language-spceific quotations, in this example we focus on the danish and swedish quotes:

  1. :lang(sv) { quotes: '\201D' '\201D' '\2019' '\2019'; }
  2. :lang(da) { quotes: '\00BB' '\00AB' '\203A' '\2039'; }
  3. q:before { content: open-quote; }
  4. q:after { content: close-quote; }

Note: We use some ISO 10646 quotation mark characters for rendering.

By looking at the results from testing this in browsers we can make the following conclusions:

  • Firefox now render language-specific quotations where specified and defaults US-EN quotations elsewhere
  • Opera also render correct quotations where specified but resorts to double quotes (0022) elsewhere
  • Safari 2 and 3 is unchanged since the quotes property is unsupported

Cross-browser Consistency

Let’s start with fixing Opera’s default quotes by adding the following CSS rule:

  1. q { quotes: '\201C' '\201D' '\2018' '\2019'; }

Now Opera and Firefox gets it right. But what about Safari? Well, the problem is that Safari ignores the quotes property and manually inserts quotation marks via the :before and :after selectors. So we need to use them as well. But we cannot simply define generic quotes by using something like q:before { content: "\201C"; } for safari, since they will override the language-specific quotes properties in firefox. Instead, let’s try this CSS code:

  1. *[lang~='da'] q:before, q[lang~='da']:before { content: '\201E'; }
  2. *[lang~='da'] q:after, q[lang~='da']:after { content: '\201C'; }
  3. *[lang~='da'] q q:before, q[lang~='da'] q:before { content: '\2019'; }
  4. *[lang~='da'] q q:after, q[lang~='da'] q:after { content: '\2019'; }
  5. *[lang~='sv'] q:before, q[lang~='sv']:before { content: '\201D'; }
  6. *[lang~='sv'] q:after, q[lang~='sv']:after { content: '\201D'; }
  7. *[lang~='sv'] q q:before, q[lang~='sv'] q:before { content: '\2019'; }
  8. *[lang~='sv'] q q:after, q[lang~='sv'] q:after { content: '\2019'; }

Since Safari has pretty good support for the attribute selector we can use the [lang~=val] selector to target any element with the lang attribute whose value is a whitespace-separated list of language. If Safari was following the specs it would be enough with one simple line to define language-specific quotes. Now we need four heavy lines for each language. Anyway, now we only have one more thing to do for Safari to get this perfect: add some nice generic quotes if no language is specified:

  1. q:before { content: '\201C'; }
  2. q:after { content: '\201D'; }
  3. q q:before { content: '\2018'; }
  4. q q:after { content: '\2019'; }

Now Opera, Safari and Firefox render the quotations correctly. Please be aware of that the CSS above will override the quotes property defined for Firefox earlier, so you could simply remove those if you like. But it might be a good idea to keep them as a reference to what should have been the correct way to render quotes. I would recommend to stick to the actual language you will use for the specific project.

What about blockquotes?

There are several ways to add quotation marks to the blockquote element. But first we need to ask ourselves: Should we add quotations to blockquotes? This is what the specs say:

We recommend that style sheet implementations provide a mechanism for inserting quotation marks before and after a quotation delimited by BLOCKQUOTE in a manner appropriate to the current language context and the degree of nesting of quotations. However, as some authors have used BLOCKQUOTE merely as a mechanism to indent text, in order to preserve the intention of the authors, user agents should not insert quotation marks in the default style. The usage of BLOCKQUOTE to indent text is deprecated in favor of style sheets.

So, W3C recommends that we add quotation marks to the blockquote, as long as we use it in a correct way (not to indent text). So let’s find out how we can do this. One way is to add double quotes around any element that is the first descendants of the blockquote and then single quotes around any q element inside by using some fancy selectors:

  1. blockquote * { quotes: ''; }
  2. blockquote > *:before { content: '\201C'; }
  3. blockquote > *:after { content: '\201D'; }
  4. blockquote q:before { content: '\2018'; }
  5. blockquote q:after { content: '\2019'; }

This should work in all modern browsers, including safari. The code will insert quotes before and after any element that is a direct child of the blockquote and then single quotes around any q element that are descendants to the blockquote. The first rule is necessary to reset the quotes in firefox before inserting them.

Now we just have to make language specific blockquote quotations!

Wrapping it up

It requires a lot of code to get the quotes right, so a general advice: stick to the actual language for the specific project and be aware of browser glitches.

Kitchen | Buttons | Avant Garde | HSL