Skip to content

[css-inline-3] initial-letters; feedback from implementation #4171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
faceless2 opened this issue Aug 4, 2019 · 7 comments
Closed

[css-inline-3] initial-letters; feedback from implementation #4171

faceless2 opened this issue Aug 4, 2019 · 7 comments
Labels
Closed Accepted by CSSWG Resolution Commenter Satisfied Commenter has indicated satisfaction with the resolution / edits. css-inline-3 Current Work i18n-tracker Group bringing to attention of Internationalization, or tracked by i18n but not needing response.

Comments

@faceless2
Copy link

faceless2 commented Aug 4, 2019

We've just been working on initial-letters and have some feedback. As far as I'm aware it's only webkit (with a prefix) offering a partial implementation on this (is this true?). So I realise this specification is in its early days. Here's what we've come up with:

1. Allow shape-margin

For initial-letters-wrap: all, there's a definite need for the shape-margin property, which should apply either to the shape manually specified with shape-outside or to shapes derived from the glyph outlines. Relying on the end-inline margin is not not flexible enough, say for letters like "Γ" (greek capital Gamma) where you want to push text away from the protruding horizontal stroke, or "C", where without a margin the text could disappear into the mouth of the "C"

2. Auto-sizing of element and effect on linebox

Inline initial-letters are auto-sized around the visible bounds of the content; around the size of the glyphs. What's not clear if whether this sizing algorithm should affect the linebox or not:
e
left-most: our rendering, where we auto-size the initial-letters elements as specified, but we don't push the linebox up to accomodate it.
middle: webkit's rendering, where the box extends down as far as required, but never goes above the cap-height of the letter.
right: a third option - the element is sized to fit the content, and the linebox is pushed up to include it.

You could make coherent arguments for the left or right renderings fairly easily; probably the left is more useful, as otherwise the height of the first line (and therefore its apparent position relative to the top of the block) will depend on its initial letter, which is likely to annoy someone at some point. I'm not sure Webkits approach will work for non-latin scripts; it's not testable as Webkit doesn't seem to support anything other than alphabetic alignment. Which brings me neatly to my next point.

3. Alignment and font-sizing

Alignment and sizing are two sides of the same issue for initial-letters. The specification goes to some length to describe how the letters have two alignment points - a two-line drop letter "A" would have its top top alignment at the cap-height of line one, and its bottom alignment at the baseline of line two, as shown with this example from the spec:

But this description only makes sense for fully dropped letters, i.e. where sink==size, and it necessarily fails if the lines aren't all the same size as the block element's line-height, as demonstrated in https://drafts.csswg.org/css-inline/#initial-letter-block-position.

So while this description is helpful to understand the intention of the specification, I think it should be clear it's no more than that. When you come to implement it, you're positioning the text on the first line without any knowledge of what's to come. Layout necessarily goes like this:

  1. Create the text with a font-size derived from the algorithm given in https://drafts.csswg.org/css-inline/#sizing-initial-letters (aside: would this algorithm vary depending on the chosen alignment baseline? I haven't worked this through yet, but this should be verified).
  2. Align the initial-letters subtree on the first line, using the normal inline alignment rules - i.e. for alphabetic alignment, align the alphabetic baseline of the initial-letter with the alphabetic baseline of the linebox.
  3. Shift the letter up or down by an appropriate amount based on the "sink" value from initial-letters.

For alphabetic text this is effectively what the spec says already. But when aligning on a hanging baseline (for example), the outsize letter will effectively drop without any further shift.

hindi

The Hindi initial-letter was aligned on the hanging baseline of the first line. That's all. No further shift was required. In fact with dominant-baseline: hanging on the paragraph and alignment-baseline: baseline on the first-letter, there's no need for the initial-letters-align property to exist at all.

I would suggest dropping it completely and simply using alignment-baseline exactly as it is defined for regular inline elements, although perhaps disallowing top, center and bottom as values.

"But what about atomic inlines", you may ask?

4. Atomic inlines

Atomic inlines such as images can be used as an initial letter as the spec is now, and the intention appears to be that they're aligned based on the same alignment points derived from the initial-letters property. But the spec is silent on the mechanism for this. I presume the height is to be set then the width scaled to match? For example, if using an image:

<p>
 <img style="initial-letters: 3" src="fancy-I.png"/>nitial letters can be images too
</p>

then the image will need to be scaled to cover three lines, and its top aligned with the top of the first linebox. Again there's no need for initial-letters-align: the "border-box" value of this property looks like it is intended to set the position and size of the image to be identical to the layout results you'd expect from this:

<p>
 <img style="alignment-baseline:top; height:3lh" src="fancy-I.png"/>nitial letters can be images too
</p>

or, for that matter (and discounting the differences in break opportunities):

<p>
 <img style="float: start; height:3lh" src="fancy-I.png"/>nitial letters can be images too
</p>

The same process applies to elements with display:inline-block and other inline content generating its own formatting context (see also #4116)

To be resolved then: does use of an atomic inline within an initial-letters element automatically set the atomic inline's height?

5. More flexibility in initial-letters values

The initial-letters element itself takes two numbers, the first ("size") determining the font-size via quite a complex algorithm, the second ("sink") the number of lines to shift up and down - not directly, as shown in the hindi example above; it's more a statement of intention as to where the bottom of the drop-cap should be.

As it is now, the "sink" value must be a positive integer and defaults to the "size" value. I don't see why this limitation is required. Make it a number or length describing the vertical shift of the initial-letter. If specified as a number, treat it as if it had an implied "lh" unit. If undefined, the value depends on the baseline: for alphabetic, it's the same as "size", for hanging or hebrew it's 0. And allow it to be less than zero if that's what the user wants.

Why? First, (like @tabatkins I believe) I dislike unitless numbers; make them a length so you can do interesting things with calc(). Second, by making this number explicitly "the distance to shift the text" it removes any confusion around the fact that hanging baseline text is dropped by default, whereas alphabetic baseline text is not. It also opens up other alignment options, such as center or mathematical which might need finer adjusting (I can't think of a use for these, but I can't think of a reason to disallow it either). Finally, it's no harder to implement.

Thought of as a length, its functionality is identical to baseline-shift. I'll leave that out there, perhaps greater minds than mine will have some ideas on reconciling the two. If there's an appetite to remove the "sink" parameter from initial-letters completely and replace it with baseline-shift, I don't see why that wouldn't work.

6. Non-replaced inlines as part of the initial-letter

The idea is you can have an initial-letter made up of more than just a single text node. It is described in the specification and even an example given, with image:

This corresponds to

<em style="initial-letters:2"><b>This</b> phrase</em> ...

While I think this isn't a bad idea, the problem will come with the font size. The spec states that initial-letter sets the used font-size only, not the computed size. Well, this becomes a problem when you have more than one element. The nested <b> above would inherit the computed font size, not the used size: effectively it's getting the font-size of the parent block.

You could insist that font-size is calculated with the magic algorithm for the entire subtree, but that seems unnecessarily restrictive. I can't think of many situations where this would be an actual problem; there may be better examples in other languages, but in English the best I can do is:

<p>
<span style="initial-letters:3">1<sup>st</sup></span> November
has an initial letter with two font sizes, and a baseline-shift based on the font-size.
</p>

Frankly, for all the hoops you would need to jump through to fix this, you may as well just say that using the initial-letters property on an element sets the computed font size, not just the used one. I'm not sure what problem the current specification was trying to avoid by not doing this, but I'm not sure it's worth it.

7. Width or height set explicitly on the initial-letters element.

The spec explicitly allows to have width or height specified, and section 5.7.1 describes how to align content within this box if required, using text-align or align-content.

Personally I think this is a very bad idea. Inline, non-replaced elements do not take a width or height anywhere else in CSS, and I think there's no need to make them do so here. If you want to give the box a particular size, just use display to set to inline-block or inline-flex. If you're setting initial-letters on a <span> this is trivially allowed; allowing limited values of display to be set on a ::first-letter pseudo-element is perhaps not an insurmountable issue either. I think this approach would be easier to understand and implement than describing some special, initial-letters only alignment rules for elements with display: inline.

8. font-size

Once you've done all the above, you've reduced initial-letter from a special thing with complex layout rules, to not much more than a regular inline: positioned like an inline, excluded like a float. The only particularly special feature remaining is the font-size. For even more flexibility at the cost of virtually nothing from an implementers point of view, why not set the font-size to its magically calculated value when initial-letters is set, but allow the user to override this if they want? i.e. if they specify font-size it is respected, but if it's left unset, its value is computed using the initial-letters algorithm.

I don't have a real use-case for this, but again I can't give a reason why it needs to be prevented. So why not?

Summary

Lots there, but to sum up my suggestions would be:

  • allow shape-margin on initial-letters
  • decide whether the inline-element size has any impact on the linebox size, and state the decision explicitly.
  • don't disallow alignment-baseline and baseline-shift on initial-letters; use them as you would normally for inline content, and instead drop the initial-letters-align property.
  • make the "sink" component of initial-letters the vertical distance to shift the letter (a length), rather than the number of lines the initial letter should sink (a unitless value). Set its default based on the initial-letters "size" and the alignment.
  • don't allow width and height on initial-letters with display:inline. If these are required, the user can use inline-flex or inline-block.
  • Make the magical value of font-size a computed value, inherited as normal and impacting on "em" units, rather than just a used value.
  • Allow user-specified font-size to override magic font-size calculation.
  • Does an atomic inline have its height set automatically? By what mechanism?
@ewilligers ewilligers added the css-inline-3 Current Work label Aug 27, 2019
@xfq xfq added the i18n-tracker Group bringing to attention of Internationalization, or tracked by i18n but not needing response. label Sep 1, 2019
@litherum
Copy link
Contributor

#4116 might be able to solve 4.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-inline-3] initial-letters; feedback from implementation, and agreed to the following:

  • RESOLVED: Use shape-margin on initial letter when shape-outside is none and `inlitial-letters-wrap: all`
The full IRC log of that discussion <RossenF2F> Topic: [css-inline-3] initial-letters; feedback from implementation
<RossenF2F> github: https://github.com//issues/4171
<emilio> faceless: we implemented initial-letters, it works broadly
<emilio> ... I think it should be simplified to work in non-latin baselines
<emilio> ... the way it's currently defined is that you specify the number of lines that is supposed to cover and the amount that it's supposed to shift
<emilio> ... the second parameter is redundant
<chris> which was the non-Safari implementation mentioned in passing? I couldn't hear
<emilio> ... we already got the ability to shifts baselines up / down using baseline-shift
<emilio> fantasai: I think there are a bunch of issues in this issue and we should take one at a time
<emilio> faceless: Sure. you can't add margins around the initial-letter... You can add some padding to the right
<emilio> ... but it's not great with varying shapes
<emilio> fantasai: so proposal would be to apply shape-margin only when wrapping around the glyph shape
<dbaron> sounds reasonable
<emilio> astearns: so for the rest we apply shape-outside
<emilio> fantasai: interaction between shape-outside and initial-letter is tbd
<emilio> ... but you can wrap around the glyph
<emilio> astearns: makes sense to use shape-margin when shape-outside is none and `initial-letters-wrap: all`
<florian> sounds good to me
<emilio> RESOLVED: Use shape-margin on initial letter when shape-outside is none and `inlitial-letters-wrap: all`
<fantasai> (we're skipping issue 2 in the issue, because it's covered in https://github.com//issues/719 )
<astearns> s/all/all and first/
<emilio> faceless: (describes issue 3 in #719)
<emilio> faceless: I think the text about the alignment points is not relevant
<emilio> ... once you drop the letter on the first-line it fits right
<emilio> fantasai: you need that for shifting caps, that's what it's for
<fantasai> s/shifting/raised or sunk/
<dbaron> I think this doesn't work both because it's hard to match the correct size, and because you need to control which lines wrap around the initial letter.
<emilio> s/that/the second value of `initial-letters`/
<emilio> faceless: I can try to demonstrate you can achieve the same effects without that value
<emilio> hober: I'd really love it if didn't file giant issues like this so that we can clear where the discussion is separately
<tantek> +1 hober
<emilio> ... this style of bug-filing is very hard to follow
<emilio> RossenF2F: That being said all the detail is awesome
<emilio> faceless: Let's move to issue 4... You can have atomic inlines as an initial letter, is that allowed? Otherwise it needs to be clarified how to align these
<chris> q+
<emilio> ... we discussed about having baselines on svg which would solve this problem, probably
<emilio> fantasai: we have an attempt to synthesize baselines for boxes
<emilio> ... so cap-height corresponds to top-height of the box
<RossenF2F> q?
<RossenF2F> ack chris
<emilio> chris: this has come up before for images
<fantasai> https://drafts.csswg.org/css-inline-3/#initial-letter-box-size
<emilio> myles: #4116 is the baselines in images
<fantasai> For atomic initial letters, sizing follows the usual rules for that type of atomic inline. However, if the box has an automatic block size (auto), then its block size is determined as for an inline initial letter with border-box alignment, and is definite.
<emilio> ... issue I filed a bug
<emilio> s/a bug/ a while ago
<chris> https://github.com//issues/4116
<emilio> iank_: this is not only for images right? Only atomic inlines
<emilio> other atomic inlines too*
<emilio> fantasai: I think the spec says how to size the box
<emilio> ... so if you set an explicit size on an atomic inline you get that size
<emilio> ... if the size doesn't match the amount of space you use the alignment properties to align it in that space
<emilio> faceless: I suspect that'd probably do it for now but probably should be explicit about how baselines work
<emilio> fantasai: agreed
<emilio> faceless: issue 5
<emilio> ... the spec allows for inlines as initial-letter
<emilio> ... the initial letter uses font-size not the computed size
<emilio> ... so if you use superscripts or what not it produces really odd result
<emilio> *results
<fantasai> scribenick: fantasai
<fantasai> on issue 6
<fantasai> faceless: If you have inlines as part of inial leter, e.g. a superscript in 1st, it doesn't scale up with the rest of the initial letter
<fantasai> emilio: what you're proposing is some weird inheritance behavior in first-lin
<fantasai> emilio: if you have span in first-line, it inherits from ::first-line
<fantasai> fantasai: There's regular element initial letters, and ::first-letter first-letters
<fantasai> fantasai: If can't have element in ::first-letter, then it's not a prolem there
<fantasai> fantasai: but for regular elements, shouldn't be a problem to inherit like usual
<fantasai> faceless: Should set the computed font size based on initial-letter, so that it will inherit as usual and affect the children
<fantasai> emilio: Not great, because a lot of stuff depends on font-size right now
<fantasai> emilio: would need t compute based on initial-letter
<fantasai> dbaron: Might be able to specify the desired results wihtout changing computed value of font-size
<fantasai> dbaron: might be more difficult to specify, but more realistic to implement
<fantasai> emilio: Can you set the initial-letter size in em units
<fantasai> faceless: no, it's derived from the algorithm
<fantasai> florian: The algorithm gives you the siz eof the glyph, if we say therefore this must affect the font size, do browsers agree on which font size you get from that
<emilio> fantasai: you have requirements for that calculation, to use specific metrics
<emilio> ... like, my line-height is X or Y, and the alphabetic-baseline
<emilio> ... that's all deterministic
<emilio> ... assuming everyone pulls the same metrics
<emilio> florian: is there a really a single font-size that gets you the right size?
<fantasai> fantasai: yes
<fantasai> fantasai: there's only one cap height metric
<fantasai> emilio: But the computed line height depends on the computed font size?
<dbaron> dbaron: modulo rounding issues
<fantasai> emilio: think it's more reasonable to not affect the computed value and then do something, but...
<fantasai> florian: Don't need the line-height/font-size of the initial letter, but of the paragraph
<RossenF2F> q?
<fantasai> emilio: but not easy to find that value at the time you're doing style computation
<dbaron> https://wiki.csswg.org/spec/property-dependencies
<fantasai> emilio: You cannot get to the containing block, you can get to the nearest display: contents thing...
<emilio> non-display: contents thing
<fantasai> dbaron: It's not super clear to me whether what you're proposing would make the computed value of font size on layout
<fantasai> dbaron: but even if it's not, still refer you to this dependency chart
<fantasai> dbaron: if you're proposing to add something, make sure it doesn't create a cycle
<fantasai> dbaron: font-size is one of the most basic dependencies
<fantasai> faceless: It definitely doesn't depend on layout
<fantasai> florian: Computed font size of initial-letter would not affect the lineh height of the parent paragraph, so no circularity
<fantasai> florian: The computed line-height of the initial letter affects nothing, so the loop stops here
<fantasai> emilio: affects nothing if you don't add lh units or other things that are proposed
<fantasai> florian: If you're trying to set the border of your initial-letter to 0.1lh, you can use it, but won't have ripple effects that mess up everything
<fantasai> emilio: I still think getting to the right parent paragraph's line-height is not trivial
<fantasai> florian: The alternative would be to say that within the initial-letter, a specific set of things do math against the used font-size instead?
<fantasai> emilio: You would need some kind of multiplier, which seems OK
<fantasai> emilio: You need to multiply by the ratio of the font-size of the initial letter to the computed font-size
<fantasai> florian: I'm not concerned about having the ratio, I'm concerned about the whitelist of what it applies to
<fantasai> florian: Not understanding the difficulty of inheriting through...
<fantasai> fantasai: DIscuss at break?
<fantasai> issue 7
<fantasai> faceless: inline boxes don't take width/height anywhere else, doesn't seem like a good idea here
<emilio> fantasai: the reason we did this is that you can't make an atomic inline into an initial letter it doesn't resize the font
<emilio> ... the content of the inline-block that is an initial letter are not affected by that
<emilio> ... what this does is you do the resizing of the glyph as usual
<emilio> ... but then you also define the size of the box
<emilio> ... this was requested by tantek
<emilio> faceless: alright, scratch that then
<emilio> astearns: it'd be nice to have a note/example in the spec
<fantasai> s/tantek/tantek, to handle the case of colored boxes with initial letters in them, wnat the boxes to all be the same size/
<emilio> faceless: number 8
<emilio> faceless: I'm ok dropping this
<emilio> dbaron: can I suggest that the issues that are viable after this discussion should become separate issues?
<emilio> ACTION: fantasai to go through the issues after doing the spec work and split the remaining issues
<emilio> (or mentor faceless into doing so)

@zed-vector
Copy link

zed-vector commented Feb 24, 2020

About 5 years ago I made this use case:

"On the 'initial-letter' property, I have previously made the case of
wishing to give the flavour of journeyman letterpress printing, in which
'suboptimal alignment' may actually be a desired effect.

I imagined few printers had type founts that exactly spanned the height
of two-lines-of-text-plus-one-thickness-of-leading. So for two lines of
10-on-12pt text the ideal would be somewhere around 22pt, but the
printer would only have had 18 and 24pt founts to chose the drop cap
from. The 18pt character would have the same baseline as the second line
of text, so its cap height would be below the cap height of the first line.

This case would appear to be approximated to by, for example
'initial-letter: 2.75 3'. I suggest this possibility would be made more
evident
if in one of the examples a non-integer were employed for the
first argument.

Section 2.5 shows the calculation for an aligned drop initial (i.e. with
equal size and sink). What would be the calculation when the arguments
are not equal?

If 'initial letter' could additionally take some more direct sizing
argument, such as a percentage of the surrounding text size, the case
would be better met and the "tricky" sizing calculation could in that
instance be avoided."

An example showing this was added by Dave Cramer. In the current draft this is shown as the final case in Example 5 'initial-letters: 2.51 3'.

The problem is that I got the initial request wrong. Our putative printer would have packed the initial letters so that the tops of the type were aligned, not the baselines.

Is it too late to fix this error?

@faceless2
Copy link
Author

faceless2 commented Apr 22, 2020

I want to sum up where we got to at the last F2F with this unwieldily hydra-issue, close it, and raise new, manageable bite-size issues for the aspects that are still relevant.

1. Allow shape-margin
Resolved, we agreed to add shape-margin to initial letters when shape-outside is none and initial-letters-wrap: all` (#4171 (comment))

2. Auto-sizing of element and effect on linebox
This was already covered in #719, which I'd missed.

3. Alignment and font-sizing
I need to further demonstrate my case, with some convincing examples, which I will do in a followup issue (#5015)

4. Atomic inlines
This is still unresolved. There are two aspects to this

  1. Is an atomic inline used as an initial letter scaled to fit? If so, scaled to what exactly?
  2. How is that atomic inline aligned?
    I think resolving the first without resolving the second won't be terribly useful, and the second is really the same issue (#4116) already raised and referenced by @litherum - we need proper control over the baselines in an image used as a character substitution. Once that's resolved, we can revisit this.

5. More flexibility in initial-letters values
This will follow on from point 3 above, which I'm going to cover elsewhere.

6. Non-replaced inlines as part of the initial-letter (the "computed font size" issue)
This was discussed at length, but no resolution. Emilio has considerable concerns over this change, but - as specified - changing the used but not computed font-size will lead to nonsensical results in some cases. I'll migrate this to a new issue (#4988) and leave open.

7. Width or height set explicitly on the initial-letters element.
Initial letters are only element with display:inline that can take a width and height. This seems odd to me, but it's needed apparently and was asked for specifically by Tantek, so it stays.

8. font-size (allowing it to be specified)
Was not discussed, but it's not really significant. I don't see why font-size cannot be specified on initial-letters, but until someone can make a case why its a useful thing to do it's not necessary.


@zed-vector - I edited your comment to add a link to the example you reference.

That example shows an initial-letter big enough for 2.51 lines, but dropped 3 lines. I think if you wanted it top-aligned, you'd make it 2.51 lines high and dropped 2.51 lines. This isn't possible with the current syntax, no, but I'll migrate this comment to my followup.

@fantasai
Copy link
Collaborator

Yo @faceless2 ! Don't close issues with resolutions until they've been edited in. :) Almost lost track of the 'shape-margin' one here.

@faceless2
Copy link
Author

Ack ;-) Sorry.

@fantasai
Copy link
Collaborator

Edited in now, so closing. ;)

@fantasai fantasai added the Commenter Satisfied Commenter has indicated satisfaction with the resolution / edits. label May 27, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Closed Accepted by CSSWG Resolution Commenter Satisfied Commenter has indicated satisfaction with the resolution / edits. css-inline-3 Current Work i18n-tracker Group bringing to attention of Internationalization, or tracked by i18n but not needing response.
Projects
None yet
Development

No branches or pull requests

8 participants