CSS quotes Property with open-quote and close-quote

The CSS quotes property defines the specific characters used for quotation marks, while the open-quote and close-quote values for the content property determine where those marks appear. Working together, they allow developers to implement language-sensitive, automatically nested quotation marks across HTML documents without hardcoding quotation punctuation directly into the markup.

The Relationship Between quotes and content Values

The quotes property acts as a dictionary or lookup table of characters, and the open-quote and close-quote values retrieve the appropriate character from that lookup table based on the element's nesting depth.

Browsers maintain an internal quote-nesting level counter. Whenever an element invokes open-quote, the browser displays the opening quote designated for the current depth and increments the nesting counter. When close-quote is invoked, the counter decrements, and the matching closing quote for that depth is rendered.

Defining the quotes Property

The quotes property is applied to a parent element or globally on elements like html or body. Its value consists of pairs of strings representing the opening and closing quotation marks for each level of nesting:

/* Syntax: quotes: [open-1] [close-1] [open-2] [close-2] ...; */
q {
  quotes: "“" "”" "‘" "’";
}

In this example:

  • The first level of nesting uses double curly quotes: “ and ”.
  • The second level of nesting uses single curly quotes: ‘ and ’.

If nesting goes deeper than the number of defined pairs, the browser repeats the last defined pair. Setting quotes: none; disables quotation generation entirely.

Applying open-quote and close-quote

To render the quotation marks, use the content property on the ::before and ::after pseudo-elements. Most browsers provide this as user-agent default styling for the <q> tag, but it can be applied to any element:

q::before {
  content: open-quote;
}

q::after {
  content: close-quote;
}

Complete Working Example

<p>
  The professor said, <q>Remember the old proverb: <q>A stitch in time saves nine.</q> It applies here.</q>
</p>
q {
  quotes: "«" "»" "“" "”";
}

q::before {
  content: open-quote;
}

q::after {
  content: close-quote;
}

In this case:

  1. The outer <q> tag triggers open-quote at level 1, rendering «.
  2. The inner <q> tag triggers open-quote at level 2, rendering “.
  3. The inner <q> ends with close-quote at level 2, rendering ”.
  4. The outer <q> ends with close-quote at level 1, rendering ».

The output renders as: The professor said, «Remember the old proverb: “A stitch in time saves nine.” It applies here.»

Managing Nesting Without Printing Characters

CSS also includes no-open-quote and no-close-quote as valid content values. These values increment or decrement the quote-nesting counter without displaying any visual character.

This is useful for multi-paragraph quotations where opening quotes should appear at the start of each paragraph, but closing quotes should only appear at the end of the final paragraph:

p.quote-continuation::before {
  content: open-quote;
}

p.quote-continuation::after {
  content: no-close-quote;
}