Dropcap
This commit is contained in:
1 parent
6edc542ea5
commit
04ea39607d
6 files changed
+158
-10
No files matched your search
+102
-7
@@ -3,20 +3,23 @@
|
||||
"Times New Roman", serif;
|
||||
}
|
||||
|
||||
/* Only A-Z and a-z: the compiler puts a drop cap on a chapter only when it
|
||||
opens on a letter. */
|
||||
@font-face {
|
||||
font-family: "Day Leaf Initials";
|
||||
src: url("../fonts/DayLeafInitials.woff2") format("woff2");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Both of these are links to their own id. They keep the text colour they
|
||||
had as plain numbers, since an underlined blue verse number in the middle
|
||||
of a sentence reads as part of the text. */
|
||||
.v,
|
||||
.chapter-number a {
|
||||
color: var(--bs-secondary-color);
|
||||
color: var(--bs-body-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.v:hover,
|
||||
.chapter-number a:hover {
|
||||
color: var(--bs-emphasis-color);
|
||||
}
|
||||
|
||||
.v {
|
||||
font-size: .7em;
|
||||
font-weight: 700;
|
||||
@@ -33,13 +36,99 @@
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* Three lines deep. The verse number it replaces is still there for its id
|
||||
and the keyboard, as the chapter number above already says where verse 1
|
||||
is -- the way a printed Bible drops it. */
|
||||
.dropcap {
|
||||
float: left;
|
||||
font-family: "Day Leaf Initials", var(--bs-body-font-family);
|
||||
font-size: 5em;
|
||||
line-height: .8;
|
||||
margin: .06em .08em 0 0;
|
||||
}
|
||||
|
||||
/* The first line pulled in closer to the letter than the lines below it, so
|
||||
the rest of the first word reads as part of it. .chapter makes this beat
|
||||
the list's and the poetry's own first-line indents. */
|
||||
.chapter :is(p, li):has(.dropcap) { text-indent: -.4em; }
|
||||
|
||||
/* The rest of the first line in small caps, as a lead-in from the initial.
|
||||
The letter can sit inside a span, such as .wj. */
|
||||
:is(p, li):has(.dropcap)::first-line {
|
||||
font-variant: small-caps;
|
||||
letter-spacing: .05em;
|
||||
}
|
||||
|
||||
.v:has(+ .dropcap):not(:focus-visible) {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip-path: inset(50%);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* The book's title, centred with room around it. --u shrinks with the header
|
||||
on a phone. */
|
||||
.book-header {
|
||||
--u: min(1rem, 100cqi / 22);
|
||||
container-type: inline-size;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: calc(var(--u) * 4.5) calc(var(--u) * 3.5);
|
||||
margin: 1rem 0 2rem;
|
||||
}
|
||||
|
||||
.book-header h1 {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: .6em 1.2em;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
line-height: 1.15;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
/* The lines above and below the name itself, such as "The First Book of
|
||||
Moses, Commonly Called", on lines of their own in small caps. */
|
||||
.book-header :is(.mt1, .mt2, .mt3, .mt4) {
|
||||
display: block;
|
||||
font-size: .45em;
|
||||
font-variant: small-caps;
|
||||
letter-spacing: .08em;
|
||||
margin: .3em 0;
|
||||
}
|
||||
|
||||
.book-header .mt { display: block; }
|
||||
|
||||
/* Centred between two flourishes (Group L 49), the left one mirrored so
|
||||
their curls point in at the number. */
|
||||
.chapter-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: .4em;
|
||||
font-size: 2rem;
|
||||
font-weight: 400;
|
||||
color: var(--bs-secondary-color);
|
||||
margin: 2rem 0 .75rem;
|
||||
margin: 5rem 0 .75rem;
|
||||
}
|
||||
|
||||
.chapter-number::before,
|
||||
.chapter-number::after {
|
||||
content: "";
|
||||
flex: none;
|
||||
width: 3.2em;
|
||||
height: .8em;
|
||||
/* Flex centres on the line box, which leaves room below for descenders;
|
||||
numerals have none, so this lifts the flourish to their middle. */
|
||||
translate: 0 -.1em;
|
||||
background: currentColor;
|
||||
mask: url("../ornaments/group-l-49.svg") center / contain no-repeat;
|
||||
}
|
||||
|
||||
.chapter-number::before { transform: scaleX(-1); }
|
||||
|
||||
/* The export marks these up as list items but hides the marker, indenting the
|
||||
first line instead, which is how a printed Bible sets them -- Ezra 10:18-44
|
||||
and the like are prose, not a bulleted list. Only the glossary and front
|
||||
@@ -66,6 +155,12 @@
|
||||
.poetry .line[data-indent="2"] { padding-left: 3em; }
|
||||
.poetry .line[data-indent="3"] { padding-left: 4.5em; }
|
||||
|
||||
/* A poetry line's indent is padding, which a float covers. Each line after
|
||||
the drop cap becomes its own block beside the letter, so its indent counts
|
||||
from the letter's edge; the drop cap's own line has no indent of its own. */
|
||||
.poetry .line:has(.dropcap) { padding-left: 0; }
|
||||
.poetry .line:not(:has(.dropcap)) { display: flow-root; }
|
||||
|
||||
.d {
|
||||
font-style: italic;
|
||||
color: var(--bs-secondary-color);
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 100" width="400" height="100" fill="currentColor"><path d="M59.8 3.8C59.4 3.9 57.9 4.1 56.4 4.3C54.6 4.5 52.7 5 51 5.6C49.5 6.2 47.2 7 45.8 7.4C41.2 8.9 39.7 9.8 31.7 16.3C28.6 18.8 28 19.8 24.8 27.1C24 29.1 22.9 31.7 22.4 32.8C19 40.2 20.1 56 24.4 63C25.2 64.2 26.4 66.3 27.1 67.5C27.9 69 29.2 70.6 30.6 72.2C31.9 73.5 33.4 75.3 34.1 76.1C34.9 77.1 35.7 77.9 36.8 78.5C41.2 80.9 42.8 82 44.7 83.6C47.9 86.2 49.8 87 57.2 88.7C60.8 89.5 61.4 89.6 64.9 89.6C67 89.6 70.5 89.7 72.6 89.9C77.8 90.4 79.7 90.4 83.9 89.8C85.8 89.6 89.9 89 93 88.6C96.1 88.1 99.8 87.5 101.2 87.1C107 85.6 110.1 87 111.4 91.7C112.6 95.8 113.1 96.2 116.1 96.2C120.5 96.2 130.5 90.5 136.9 84.4C139.9 81.5 142.2 80.9 146.4 82C147.5 82.3 149.2 82.5 150.1 82.5C151 82.5 152.9 82.8 154.3 83.1C158 84 159.8 83.9 166 82.2C169.5 81.3 170.6 78.4 168.9 74.5C166.8 69.6 165.4 54.5 166.6 50.1C167 48.7 168.7 44.9 169.8 43.1C170.3 42.3 170.9 40.9 171.2 40C172 37.7 172.8 36.3 174.2 34.5C174.8 33.6 175.9 31.9 176.5 30.7C177.9 27.6 178.9 26.4 181.2 24.6C182.2 23.8 183.7 22.7 184.4 22.1C188.9 18.4 197.6 14.2 205.3 12C207.2 11.4 209.8 10.6 211 10.2C217.2 8 231.5 8.1 240.2 10.3C243 11 249.2 13.9 251.5 15.4C252.7 16.2 254.9 17.6 256.5 18.3C261.5 21 266.8 25 267.8 26.9C268.6 28.3 268.5 31.4 267.6 33.1C266.8 34.8 262.5 39.4 261 40.1C257.4 41.9 252 41.1 248.7 38.2C247.7 37.4 246.5 36.7 245.4 36.3C244.4 35.9 242.5 35 241.2 34.2C239.9 33.5 238.2 32.7 237.4 32.4C236.7 32.1 234.7 31.2 233 30.5C231.4 29.7 228.5 28.7 226.7 28.2C224.9 27.7 222.1 26.8 220.4 26.2C216.3 24.8 214.9 24.8 210.3 25.8C204.7 27 201.5 28 197.9 29.7C196 30.6 194.5 31.4 194.4 31.4C193.2 31.4 188.3 35.9 186.7 38.6C186 39.8 184.8 41.8 184 43C182.4 45.4 182.2 46.2 181.7 50.3C180.8 58.6 183 66.1 188.1 72.2C190.5 74.9 192.1 76.1 197.1 78.7C201.3 81 201.3 81 208 81C214.5 81.1 214.7 81.1 217.5 80.4C225.1 78.5 226.4 78 228.9 75.8C229.8 74.9 231 73.9 231.6 73.4C232.3 73 233.5 71.9 234.5 71.1C235.4 70.3 236.9 69.3 237.8 68.7C238.7 68.2 240.1 67.2 240.9 66.5C241.7 65.8 243.2 64.5 244.4 63.6C245.5 62.7 247.3 61.1 248.5 60C254.9 54 262.5 54.8 264.9 61.7C265.5 63.2 265.8 63.6 267.8 65.5C269.6 67.1 270.4 68.1 271.2 69.5C272.9 72.6 273.6 73.4 275.2 74.1C276 74.5 277.7 75.3 279 75.9C284.2 78.2 287.7 78.4 291.6 76.3C294.8 74.7 296.4 73 298 69.9C299.5 67 299.5 67.6 297.2 58.2C295 49.6 293.3 45.6 290 41.9C282.3 33.1 282.1 30 288.7 25.7C289.8 25 291.4 23.8 292.1 23.2C292.9 22.5 294.3 21.6 295.3 21.2C296.2 20.7 298.4 19.7 300.1 18.9C303.5 17.1 313.2 13.8 317.8 12.9C320.3 12.4 320.5 12.4 322.5 12.8C327.3 13.7 331.1 15.1 335.4 17.4C339.3 19.5 341.1 21 344.1 24.9C346.9 28.5 346.8 28.2 347.6 33.9C348.4 40.4 348.3 41.3 346.5 45.9C344.6 50.9 344.5 51 342.9 52.7C342.2 53.6 341.2 55 340.7 56.1C338.8 59.9 333.8 63 326.8 64.4C325.9 64.5 324.3 65.1 323.1 65.6C320.7 66.7 319.3 67 315.4 67.4C310.5 67.8 309.7 68.5 310.6 71.7C311.2 73.6 311.3 73.8 313 75.3C314.2 76.5 315 77.4 315.8 78.9C317.1 81.3 317.4 81.7 320.7 83.9C322.1 84.9 323.8 86 324.4 86.4C326.9 88.1 328 88.4 333.2 88.5C338.6 88.6 340 88.4 346.5 86.2C351.4 84.5 351.8 84.4 353.4 84.8C355 85.2 357.1 87.3 358.6 90.3C360.1 93.1 363.3 95.6 364.8 95.3C366 95 369.6 92.2 370.3 91.1C375.5 82.9 375.4 83.1 377.2 73.7C378.2 68.8 378.2 68.8 378 63.2C377.8 52 377.9 51 378.8 47.3C380.6 39.6 379.3 31 375.7 27.1C374.5 25.9 373.1 23.8 371.7 21.5C369.9 18.4 369.4 17.7 368.1 16.8C367.3 16.1 365.8 14.8 364.8 13.8C362.8 11.7 361.2 10.7 358.2 9.4C357.1 8.9 355.2 7.8 354.1 7.1C352.7 6.1 351.8 5.6 351 5.5C350.4 5.5 348.6 5.2 346.9 5C342.1 4.4 339.7 4.4 329.2 4.6C319.2 4.8 319.2 4.8 316.8 5.6C310.3 7.7 309 8.2 307.4 9.4C306.2 10.3 304.9 10.9 302.6 11.7C298.5 13.1 296.3 14.2 293.7 15.9C292.5 16.6 290.5 17.8 289.3 18.4C288 19.1 285.9 20.3 284.6 21.1C280 24 276.1 24.3 272.2 22.1C270.1 21 266.1 18.4 263 16.3C258.5 13.2 257.7 12.8 255.2 11.9C253.9 11.5 252.2 10.8 251.4 10.3C250.6 9.8 249.5 9.1 249.1 8.8C248.2 8.3 242.9 6.2 240.4 5.4C238.6 4.8 219 3.8 208.1 3.8C203.3 3.8 198.2 4.8 191.3 7C189.4 7.6 187 8.2 186 8.4C184.5 8.6 183.6 8.8 182 9.8C180.8 10.4 178.8 11.2 177.6 11.6C176.3 11.9 174.7 12.4 174 12.6C173.4 12.8 172.2 13.4 171.5 14C169.8 15.2 168.5 15.9 166.5 16.4C165.2 16.7 164.3 17.2 162.8 18.3C160.9 19.7 159.7 20.2 157.3 20.6C156.8 20.7 155.9 21.3 155.3 21.9C154.7 22.5 153.4 23.3 152.5 23.8C150.9 24.6 149.3 26.1 146.7 29.2C146.2 29.8 144.9 31 143.9 31.8C141.8 33.4 139.1 35.7 136.1 38.6C135.1 39.6 133.6 40.7 132.9 41C131.9 41.5 131.5 42 130.6 43.4C129.3 45.6 128.2 46.5 125.8 47.4C123.6 48.1 123.3 48.3 122.5 49.5C121.6 50.9 120.5 52 119 53.1C118.2 53.7 116.7 55 115.5 56.1C113 58.5 112 59.1 109.9 59.9C108.6 60.3 107.9 60.8 106.4 62.2C105.3 63.2 103.8 64.4 102.8 64.9C101.9 65.4 100.4 66.5 99.3 67.3C98.3 68 96.4 69.2 95.1 69.8C93.7 70.5 91.6 71.7 90.3 72.5C87.5 74.3 85.9 74.9 79.7 76.4C69.2 78.9 64.5 79.2 57.4 77.8C47.4 75.8 41.6 73.3 38 69.4C37.2 68.5 35.7 67.2 34.8 66.6C31.5 64.5 30.8 63.5 28.5 57.8C26.3 52.4 26.1 51.5 26.4 43.4C26.6 33.5 27.5 31.4 34.5Line truncated
|
||||
|
After Width: | Height: | Size: 8.8 KiB |
@@ -23,6 +23,8 @@ const PRECACHE = [
|
||||
'./manifest.webmanifest',
|
||||
'./css/app.css',
|
||||
'./css/bootstrap.min.css',
|
||||
'./fonts/DayLeafInitials.woff2',
|
||||
'./ornaments/group-l-49.svg',
|
||||
'./js/htmx.min.js',
|
||||
'./js/bootstrap.bundle.min.js'
|
||||
].concat(TEMPLATE_URLS);
|
||||
|
||||
@@ -44,6 +44,7 @@ class Handler(SimpleHTTPRequestHandler):
|
||||
".png": "image/png",
|
||||
".svg": "image/svg+xml",
|
||||
".ico": "image/x-icon",
|
||||
".woff2": "font/woff2",
|
||||
}
|
||||
server_version = "bible-static"
|
||||
sys_version = ""
|
||||
|
||||
+52
-3
@@ -453,11 +453,51 @@ def ref_href(node, book, chapter):
|
||||
return "%s#%s" % (base, verse_id(target_ch, verse))
|
||||
return "%s#ch%d" % (base, target_ch)
|
||||
|
||||
DROPCAP_BLOCKS = ("paragraph", "line", "item")
|
||||
|
||||
def with_dropcap(nodes):
|
||||
"""Split the first letter of the first text off into a dropcap node.
|
||||
|
||||
Returns the new node list, or None when the text does not start with a
|
||||
letter. The initials font has only A-Z and a-z, so a chapter opening on
|
||||
a quotation mark or a digit gets no drop cap rather than a fallback one.
|
||||
"""
|
||||
nodes = list(nodes)
|
||||
for i, node in enumerate(nodes):
|
||||
kind = node["t"]
|
||||
if kind == "verse":
|
||||
continue
|
||||
# A note caller would land between the letter and the rest of its word.
|
||||
if kind == "noteref":
|
||||
return None
|
||||
if kind == "text":
|
||||
text = node["v"].lstrip()
|
||||
if not text:
|
||||
continue
|
||||
if not ("A" <= text[0] <= "Z" or "a" <= text[0] <= "z"):
|
||||
return None
|
||||
nodes[i:i + 1] = [{"t": "dropcap", "v": text[0]},
|
||||
dict(node, v=text[1:])]
|
||||
else:
|
||||
inner = with_dropcap(node.get("c", []))
|
||||
if inner is None:
|
||||
return None
|
||||
nodes[i] = dict(node, c=inner)
|
||||
# The verse number before the drop cap would otherwise sit between
|
||||
# the floated letter and the rest of its word.
|
||||
for j in range(i):
|
||||
if nodes[j]["t"] == "verse":
|
||||
nodes[j] = dict(nodes[j], dropcap=True)
|
||||
return nodes
|
||||
return None
|
||||
|
||||
def render_inline(nodes, book, chapter, out):
|
||||
for node in nodes:
|
||||
kind = node["t"]
|
||||
if kind == "text":
|
||||
out.append(esc(node["v"]))
|
||||
elif kind == "dropcap":
|
||||
out.append('<span class="dropcap">%s</span>' % esc(node["v"]))
|
||||
elif kind == "verse":
|
||||
label = esc(node["label"])
|
||||
vid = verse_id(chapter, node["label"])
|
||||
@@ -466,10 +506,13 @@ def render_inline(nodes, book, chapter, out):
|
||||
# that dialog. Being a real link is also what gives it the
|
||||
# keyboard and context menu a reader expects.
|
||||
out.append('<a class="v" id="%s" href="#%s" '
|
||||
# A non-breaking space keeps the number on the same
|
||||
# line as the word it introduces, as the source does.
|
||||
'aria-label="Verse %s, options">%s</a> '
|
||||
'aria-label="Verse %s, options">%s</a>'
|
||||
% (vid, vid, label, label))
|
||||
# A non-breaking space keeps the number on the same line as the
|
||||
# word it introduces, as the source does. Before a drop cap the
|
||||
# number is hidden and the space would only push the word away.
|
||||
if not node.get("dropcap"):
|
||||
out.append(" ")
|
||||
elif kind == "noteref":
|
||||
n = node["id"].replace("FN", "")
|
||||
out.append('<a class="noteref" id="fnref%s_%s" href="#fn%s_%s">'
|
||||
@@ -550,8 +593,14 @@ def render_chapter(chapter, book, out):
|
||||
out.append(" </ul>\n")
|
||||
group = None
|
||||
|
||||
dropcap = True
|
||||
for block in chapter["blocks"]:
|
||||
btype = block["type"]
|
||||
if dropcap and btype in DROPCAP_BLOCKS:
|
||||
dropcap = False
|
||||
content = with_dropcap(block.get("content", []))
|
||||
if content is not None:
|
||||
block = dict(block, content=content)
|
||||
if btype == "break":
|
||||
close_group()
|
||||
continue
|
||||
|
||||
Reference in new issue
Block a user