Visitar URL original
[css-text] Reconsidering the CSS letter-spacing model · Issue #10193 · w3c/csswg-drafts · GitHub
Skip to content

[css-text] Reconsidering the CSS letter-spacing model #10193

Open

Description

@jfkthame

Summary

  • CSS letter-spacing is a long-established and widely-used feature;
  • Behavior of existing letter-spacing implementations is unsatisfactory;
  • There is incompatibility between Gecko and WebKit/Blink, with neither approach being clearly better;
  • The behavior currently described in the spec would be better, but no browser implements it;
  • Changing to the spec'd behavior would carry an unacceptable level of compat risk;
  • I propose revising the spec to describe behavior that:
    • resolves the undesirable asymmetry of current implementations;
    • allows browsers to converge on a common behavior that is better than either existing version;
    • carries minimal compat risk as there will be no wider impact on layout.

Proposed spec change

Change the description of letter-spacing from

Specifies additional spacing between typographic character units. Values may be negative, but there may be implementation-dependent limits.

to

Specifies additional spacing applied to each typographic character unit except those with zero advance. The additional spacing is divided equally between the inline-start and -end sides of the typographic character unit. Values may be negative, but there may be implementation-dependent limits.

Much of the following explanation and the associated examples can then be considerably simplified.

Background

The current CSS spec for letter-spacing bears little relation to the behavior actually seen in browsers. This has been the case “forever”, and no browser seems sufficiently interested in implementing the model described by the specification to overcome implementation inertia and webcompat fears. See for instance the extensive (and inconclusive) discussion in #1518.

Typical use cases for the letter-spacing feature include:

  • Showing e m p h a s i s of some inline text within a block (an alternative presentation to italics or boldface) or as a heading;
  • Adjusting the spacing of text for better legibility, e.g. spreading the glyphs out slightly at small font sizes, or tightening the spacing at a large headline size; in principle, this could be handled by optical sizing with a variable font, but not all fonts offer this;
  • As part of a logo, branding, etc., where the text is used as a design element and characters may be deliberately squashed together (see the W3C logo) or spread apart for visual interest.

The spec currently says that “the total letter spacing between two adjacent typographic character units (after bidi reordering) is specified by and rendered within the innermost element that contains the boundary between the two typographic character units”. It goes on to give examples of what this implies: e.g. given

p    { letter-spacing: 1em; }
span { letter-spacing: 2em; }

<p>a<span>bb</span>c</p>

the increased letter-spacing of the span should apply only between the two “b”s, not between “a” and “b” or between “b” and “c”.

None of Firefox, Chrome or Safari behave this way. In effect they all apply added spacing to the individual characters based on the letter-spacing value of the current element, not to the boundaries based on the innermost containing element. So both “b”s get extra space after them.

One of the key shortcomings of current implementations is their asymmetry, whereby letter-spacing is applied on only one side of each affected character. This means that if letter-spacing is applied to a s i n g l e  word, for example, the following inter-word space will also grow, but the preceding one will not.

The model proposed in the current spec would resolve this, though its implication that letter-spacing applied to a single character in isolation has no effect would be surprising to authors, and more generally, the change from long-established behavior carries significant compatibility risks. Text measurement and line-breaks in existing documents would change, potentially resulting in layout shifts that could sometimes be quite drastic. (Four years ago in #1518 (comment), Koji noted that “For Blink … the breakage is beyond what we can accept”.)

A way forward

I think rather than continuing to describe a letter-spacing model that is not actually found in browsers, the spec should be changed to reflect reality. So letter-spacing functions as an attribute of the characters in the text, just like font-family or -size, and is applied to each typographic character unit individually according to its computed value of the property. This is what the engines actually do, and I believe it corresponds to a typical typographer’s mental model, which is that letter-spacing (or tracking) represents an adjustment applied to the advance widths of the glyphs; it’s equivalent to using a modified version of the font. In particular, if letter-spacing is applied to a single character within a text (abc), users would expect to see some effect.

One issue where engines currently differ is in the treatment of RTL text. Chrome and Safari always add letter-spacing on the right-hand side of the character, while Firefox adds it on the trailing side, hence for RTL content it appears on the left. The Chrome/Safari behavior is arguably preferable for content with mixed directions, but seems undesirable for content that is primarily or exclusively RTL. Neither approach is entirely satisfactory.

Some simple examples are shown in https://codepen.io/jfkthame/pen/vYMjrwe. Screenshots of the current rendering in WebKit/Blink (left) and Gecko (right) show the unbalanced results of the asymmetrical implementations:

image

Even for purely LTR content, applying letter-spacing entirely on the right-hand edge of the characters is less than ideal. As no browser currently implements trimming of the letter-spacing at end of line (as the spec suggests they should), a block of justified, right-aligned, or centered text ends up visually offset (to the left, assuming positive letter-spacing).

A more balanced result would be achieved, and the problems of mixed-direction content resolved, if the letter-spacing for each character were divided evenly between its two sides. This would mean that blocks of text with letter-spacing would be evenly inset from both margins, instead of offset.

In this model, where letter-spacing is an attribute of each character, what happens at element boundaries is obvious, and requires no special handling: the total spacing between the characters is simply half of the computed letter-spacing of the character before the boundary, and half of the computed letter-spacing of the character after the boundary.

The alternative proposed here thus addresses the asymmetry issues, but with much less compatibility risk than the spec’s current model: overall text measurement and layout is unaffected compared to existing behavior. All that changes is that glyphs appear centered within their (letter-spacing-adjusted) advance width instead of aligned to one edge (either left or inline-start, depending on the engine) of it.

With this simple change, the examples in the above codepen look much better:

image

I believe this would be straightforward to implement in existing browser engines. In many real-world uses where letter-spacing is small the change may well go unnoticed. In cases like naively-applied German-style  e m p h a s i s  it will often be an improvement because of its symmetry; likewise in bidi content it will usually be a cosmetic improvement. It’ll only be a (cosmetic) regression in cases where an author has taken the trouble to explicitly account for the asymmetry of the existing behavior, e.g. by specifying a compensating margin on one side of the element only. Such cases would look subtly worse as the adjustment will no longer be appropriate, but I think this level of “breakage” should be relatively harmless.

Activity

  1. xiaochengh commented on Apr 16, 2024

    @xiaochengh
    Contributor

    The alternative proposed here thus addresses the asymmetry issues, but with much less compatibility risk than the spec’s current model: overall text measurement and layout is unaffected compared to existing behavior. All that changes is that glyphs appear centered within their (letter-spacing-adjusted) advance width instead of aligned to one edge (either left or inline-start, depending on the engine) of it.

    I have concerns about this, as it breaks alignment. Currently, a paragraph of text with letter-spacing still appears aligned to the inline-start edge of the block (at least on LTR pages), but with the proposal, the text will be off by half of the letter spacing.

    A real example is this Hong Kong gov website. With the proposal, the main text, which has letter spacing, will no longer be left-aligned with the header text.

    And I don't see an easy fix. We can manually apply some shifting to the text, but it doesn't sound like the right approach.

  2. jfkthame commented on Apr 16, 2024

    @jfkthame
    ContributorAuthor

    Yes, this does indeed break alignment in such cases. I would suggest, though, that the "breakage" is pretty insignificant. The amount of letter-spacing used in the main text of that HK gov site seems quite unusual to me; I rarely see so much spacing applied to blocks of body text. Yet in my opinion, the result still looks perfectly acceptable:

    image

    It's true that the main text is not perfectly left-aligned with the header, but I suspect most users won't even notice this.

    I think this is a reasonable price to pay for getting more consistent behavior in right-aligned or centered content, bidi cases, etc.

    (Incidentally, that page also includes an example of how symmetrical letter-spacing visibly improves results: with left-aligned letter-spacing, the caption under the photograph appears misaligned:
    image
    whereas with symmetrical spacing, we get:
    image
    which looks better balanced.)

  3. xiaochengh commented on Apr 17, 2024

    @xiaochengh
    Contributor

    The amount of letter-spacing used in the main text of that HK gov site seems quite unusual to me; I rarely see so much spacing applied to blocks of body text.

    From my years of experience living in HK, such kind of large spacing is used quite a lot in Chinese text in HK.

    Since letter-spacing is such an old feature with pretty stable behavior over the years, I would prefer a more cautious approach:

    • Revise the spec to match what browsers are currently doing (at least on the interoperable part)
    • Introduce something new to specify where to apply spacing:
    letter-spacing: 1px end; /* default value */
    letter-spacing: 1px start;
    letter-spacing: 1px symmetric; /* or just "center"? */
  4. frivoal commented on Apr 17, 2024

    @frivoal
    Collaborator

    Effectively, using terminology found in other properties (ruby-align or justify-content) and along the lines of what @xiaochengh is saying, we can think of this as:

    • the spec currently describing letter-spacing: 1px space-between;
    • Firefox implementing letter-spacing: 1px space-after;
    • Webkit/Blink implementing letter-spacing: 1px space-right;
    • @jfkthame proposing letter-spacing: 1px space-around;

    The currently specified behavior remains preferable in my opinion, but if we're not going to get it, I find this to be an interesting proposal. That said, should get just one behavior, or should we give authors choice as @xiaochengh suggests? If there's choice, what's the default?

    It's pretty easy to imagine specifying something like this:

    letter-spacing: [normal | <length-percentage>] &&
                    [ space-left | space-right | space-before | space-after | space-between | space-around]?
    

    and either default to a particular value when omitted, or leave it up to the UA.

    But this seems overkill.

    An alternative could be to specify and implement the behavior proposed by @jfkthame, but also add a toggle to trim away the first and last half-spaces in the line. Making that opt-in is more compatible, opt-out gives better behavior by default. So something like:

    letter-spacing: [normal | <length-percentage>] && trim?
    

    or

    letter-spacing: [normal | <length-percentage>] && no-trim?
    

    Possibly with long-hands (letter-spacing-amount & letter-spacing-trim ?), if you want to to cascade independently.

  5. xiaochengh commented on Apr 17, 2024

    @xiaochengh
    Contributor

    An alternative could be to specify and implement the behavior proposed by @jfkthame, but also add a toggle to trim away the first and last half-spaces in the line

    This looks much better. My original suggestion does look like an overkill.

    Making that opt-in is more compatible, opt-out gives better behavior by default.

    I think making trim the default value has both better compat (as it doesn't break left alignment) and better behavior (as it fixes center alignment). The only compat risk I can think of is that line wrapping can be slightly different, but I don't see how this can be a real issue yet.

    Or if we really want to be cautious about compat, then just add an auto option to let browsers keep their current behaviors.

  6. moved this from Unsorted to Wednesday afternoon in CSSWG June 2024 meetingon Jun 3, 2024
  7. moved this from Wednesday afternoon to Unsorted in CSSWG June 2024 meetingon Jun 3, 2024
  8. moved this from Unsorted to Wednesday afternoon in CSSWG June 2024 meetingon Jun 3, 2024
  9. moved this from Wednesday afternoon to Unsorted in CSSWG June 2024 meetingon Jun 3, 2024
  10. moved this from Unsorted to Wednesday afternoon in CSSWG June 2024 meetingon Jun 3, 2024
  11. 13 remaining items

  12. added
    i18n-needs-resolutionIssue the Internationalization Group has raised and looks for a response on.
    on Jun 25, 2026
  13. r12a commented on Jun 25, 2026

    @r12a
    Contributor

    The i18n WG decided to move this to 'needs-resolution'. We would appreciate it if it could receive some attention before TPAC.

  14. frivoal commented on Aug 12, 2026

    @frivoal
    Collaborator

    The i18n WG decided to move this to 'needs-resolution'.

    We have a resolution, and the edits have just landed. What remains to do is to check compatibility, as discussed above. Idealy, we will not need to introduce an optional keyword to switch between the legacy bad behavior and the sensible one. But this needs investigation. It'd be great if one or more browser vendor were willing to take the lead here, and trial an implementation to see if anything breaks badly.

  15. thejohnjansen commented on Oct 20, 2026

    @thejohnjansen

    Removing "Agenda+" as this issue appears to need some more information before discussing in the call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions