Implement accessible drop-cap

I am implementing a design where I need to implement accessible drop caps. So I need my template to capture the first letter from the first paragraph and render it like so:

<p class="the-first-paragraph">
  <span aria-hidden="true">
    <span class="dropcap">L</span>orem
  </span>
  <span class="sr-only">Lorem</span> Ipsotum mit fantaculum...
</p>

I am pretty sure this could be handled via a short code, but this is a client project for non-technical users, who will be adding content via Forestry and the more pain points I can eliminate for them the better, so I need to handle this at the template level.

Thanks for any guidance

You can use pure CSS to achieve drop caps. See https://css-tricks.com/snippets/css/drop-caps/

p:first-child:first-letter {
  color: #903;
  float: left;
  font-family: Georgia;
  font-size: 75px;
  line-height: 60px;
  padding-top: 4px;
  padding-right: 8px;
  padding-left: 3px;
}

Thanks, for the reply. My question is not about how to implement the css or html to achieve the drop cap.

I need help
writing some logic to add to my template file to identify the first letter from the first paragraph of a given post and render the above html.

Hope that clarification is helpful.

Tbh, I didn’t understand your issue, since @atishay suggestion was valid.

I recently worked on something similar. My approach was to create a param to enable/disable the drop-cap, by adding a class to the article block, like <article class="has-drop-cap"> and leave the rest to the CSS (ie.: .has-drop-cap > p:first-of-type:first-letter).

If you insist on using that DOM structure, I’m afraid replaceRE is your only option here.

This discussion has gone off the rails, but here is my rationale, for doing it this way. I am seeking a solution that makes a fully accessible drop cap. A CSS only approach only gets a person part of the way there. @atishay copied and pasted code from the pen that does not address how a screen reader will actually announce the text. If one digs a bit further into the subject we learn that screen readers don’t do a good job or announcing w/ only a CSS approach.

So, I do insist on using that DOM structure b/c that will fulfill the a11y requirements for my project.

I take full responsibility for writing a misleading title.
thanks

Hold your horses. We are here to help and I think I gave you a solution already (even if it’s a hacky one). If you need further assistance on it, just go ahead and say it.

@sephore
I am asking for help on how to write template logic to output html in a very specific way.

For example…

layouts/_default/single.html

{{ partial "drop-cap-first-paragraph.html" .Content }}

layouts/partials/drop-cap-first-paragraph.html

{{- $content := . -}}

{{- $firstWord := index (findRE `^<p>\S+` $content) 0 | strings.TrimPrefix "<p>" -}}
{{- $firstCharacter := substr $firstWord 0 1 -}}
{{- $remainingCharacters := substr $firstWord 1 -}}

{{- $chunk := `
<p class="the-first-paragraph">
  <span aria-hidden="true">
    <span class="dropcap">@@firstCharacter@@</span>@@remainingCharacters@@
  </span>
  <span class="sr-only">@@firstWord@@</span>` -}}

{{- $chunk = replace $chunk "@@firstCharacter@@" $firstCharacter -}}
{{- $chunk = replace $chunk "@@remainingCharacters@@" $remainingCharacters -}}
{{- $chunk = replace $chunk "@@firstWord@@" $firstWord -}}

{{- replaceRE `^<p>\S+` $chunk $content | safeHTML -}}
1 Like

Oh, man. I was almost there. Eh, guess I’ll leave my attempt here anyway:

{{ $firstP := `(?:^<p>((\b[a-zA-Z])([\w\-]+))((?:.|\n)+?)</p>)` }}
{{ $withDropCapDom := printf "<p class=\"the-first-paragraph\"><span aria-hidden=\"true\"><span class=\"dropcap\">${2}</span>${3}</span><span class=\"sr-only\">${1}</span>${4}</p>" }}

{{ .Content | replaceRE $firstP $withDropCapDom | safeHTML }}

@jmooring
Thanks for your help! This worked for a hot second, however, Hugo is throwing an error:

Failed to render pages: render of "section" failed: execute of template failed: template: chapters/section.html:14:7: executing "main" at <partial "first-paragraph.html" .Content>: error calling partial: "/my-project/layouts/partials/first-paragraph.html:5:28": execute of template failed: template: partials/first-paragraph.html:5:28: executing "partials/first-paragraph.html" at <substr $firstWord 1>: error calling substr: start position out of bounds for 0-byte string

Also, pardon my ignorance, but I’m not familiar w/ the “@@aPattern@@” pattern. What is it called? I’d like to look it up.

fyi, my current version of Hugo is: v0.74.3/extended darwin/amd64

thanks again!

Yeah, I didn’t consider empty pages. Revised:

{{- $content := . -}}
{{- with findRE `^<p>\S+` $content -}}
  {{- $firstWord := index . 0 | strings.TrimPrefix "<p>" -}}
  {{- $firstCharacter := substr $firstWord 0 1 -}}
  {{- $remainingCharacters := substr $firstWord 1 -}}

  {{- $chunk := `
  <p class="the-first-paragraph">
    <span aria-hidden="true">
      <span class="dropcap">@@firstCharacter@@</span>@@remainingCharacters@@
    </span>
    <span class="sr-only">@@firstWord@@</span>` -}}

  {{- $chunk = replace $chunk "@@firstCharacter@@" $firstCharacter -}}
  {{- $chunk = replace $chunk "@@remainingCharacters@@" $remainingCharacters -}}
  {{- $chunk = replace $chunk "@@firstWord@@" $firstWord -}}

  {{- replaceRE `^<p>\S+` $chunk $content | safeHTML -}}
{{- else -}}
  {{- $content | safeHTML -}}
{{- end -}}

The @@token@@ pattern has nothing to do with Hugo or go. It’s just a pattern that I’ve been using for a long time when I need to replace substrings. There’s nothing magic about it.

1 Like

@sephore A+ for brevity! Try this:

Für viele Leute, vor allem aus dem europäischen Ausland, ist Deutschland kein Land, in dem man seine Ferien verbringen kann. In ihrer Phantasie regnet es dort immer und sie fahren lieber rund ums Mittelmeer mit seiner Sonnengarantie.

1 Like

Thanks to both @sephore and @jmooring for their help on this. Marking as solved.

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.