| 1 | package layout |
| 2 | |
| 3 | // sizes.go is internal/slides/static/slides.css evaluated at 1920 by |
| 4 | // 1080. Every number here is the stylesheet's own declaration with the |
| 5 | // unit worked out, written as the multiplication rather than as the |
| 6 | // answer so the two can be read side by side. |
| 7 | // |
| 8 | // ── What a cqw is, and why there are two of them ────────────────────── |
| 9 | // |
| 10 | // A container query length is one percent of the query container's |
| 11 | // CONTENT box, not of its border box. The section is the container and |
| 12 | // it carries `padding: 6.25%` — 120px each side at 1920 — so on a |
| 13 | // slide with padding one cqw is 16.8px, and on one without it |
| 14 | // (`image-full` and every canvas slide both set `padding: 0`) it is |
| 15 | // 19.2. That is a 12.5% difference in every type size on six of the |
| 16 | // seven templates, which is why it is spelled out twice here instead |
| 17 | // of being assumed once. |
| 18 | // |
| 19 | // Checked against Chromium rather than reasoned about: a `title` |
| 20 | // slide's h1 computes to 110.88px (6.6 × 16.8) and a canvas slide's to |
| 21 | // 126.72 (6.6 × 19.2). |
| 22 | // |
| 23 | // ── The gap between regions ─────────────────────────────────────────── |
| 24 | // |
| 25 | // `gap: var(--slides-gap)` used to be written on the section, which is |
| 26 | // the query container, and an element is never its own container: the |
| 27 | // browser resolved it against whatever was outside the slide, so the |
| 28 | // space between a heading and a body was 49.92px on a 1920 screen and |
| 29 | // 20.27px in a 780px window. Slice 3 moved the template grid onto a |
| 30 | // `[slides-frame]` inside the section (render.SlideWith writes it), so |
| 31 | // the declaration is a descendant's like every other one and the gap |
| 32 | // is 2.6 padded cqw wherever the slide is drawn. sectionGap below is |
| 33 | // that one number, and it is now the same length as scale.gap() on a |
| 34 | // padded template — spelled separately because they are two different |
| 35 | // declarations in the stylesheet and one of them could move. |
| 36 | |
| 37 | // The frame. 16:9, and the only aspect this build has (§9). |
| 38 | const ( |
| 39 | FrameW = 1920.0 |
| 40 | FrameH = 1080.0 |
| 41 | |
| 42 | // pad is `--slides-pad: 6.25%`. A percentage padding resolves |
| 43 | // against the containing block's INLINE size on all four sides, so |
| 44 | // the top and bottom of a slide are 120 as well, not 67.5. |
| 45 | pad = 0.0625 * FrameW |
| 46 | |
| 47 | // cqwPadded is one cqw inside a section with padding, cqwFull one |
| 48 | // inside a section without it. |
| 49 | cqwPadded = (FrameW - 2*pad) / 100 |
| 50 | cqwFull = FrameW / 100 |
| 51 | |
| 52 | // sectionGap is `[slides-frame] { gap }` — see the header. |
| 53 | // sectionColGap is two-column's |
| 54 | // `column-gap: calc(var(--slides-gap) * 1.6)`, on the same |
| 55 | // element and so resolved the same way. |
| 56 | // |
| 57 | // Padded, because every template that has more than one region |
| 58 | // keeps the section's padding: image-full is the one that does |
| 59 | // not, and its frame sets `gap: 0`. |
| 60 | sectionGap = 2.6 * cqwPadded |
| 61 | sectionColGap = sectionGap * 1.6 |
| 62 | ) |
| 63 | |
| 64 | // scale is the type and spacing scale at one cqw. Everything a |
| 65 | // DESCENDANT of the section is sized in goes through one of these; the |
| 66 | // section's own two declarations are the constants above. |
| 67 | type scale struct{ cqw float64 } |
| 68 | |
| 69 | var ( |
| 70 | // padded is the scale on the five templates that keep the |
| 71 | // section's padding, and full the one on `image-full` and on every |
| 72 | // canvas slide. |
| 73 | padded = scale{cqwPadded} |
| 74 | full = scale{cqwFull} |
| 75 | ) |
| 76 | |
| 77 | // scaleFor is the scale a slide is drawn at: which of the two above |
| 78 | // depends on whether the section has padding. |
| 79 | func scaleFor(layout, template string) scale { |
| 80 | if layout == "canvas" || template == "image-full" { |
| 81 | return full |
| 82 | } |
| 83 | return padded |
| 84 | } |
| 85 | |
| 86 | // The type scale, from `[slides-slide]`'s custom properties. |
| 87 | func (s scale) t1() float64 { return 6.6 * s.cqw } // --slides-t-1 |
| 88 | func (s scale) t2() float64 { return 5.0 * s.cqw } // --slides-t-2 |
| 89 | func (s scale) t3() float64 { return 4.0 * s.cqw } // --slides-t-3 |
| 90 | func (s scale) bodyL() float64 { return 3.4 * s.cqw } // --slides-body-l |
| 91 | func (s scale) bodyM() float64 { return 2.8 * s.cqw } // --slides-body-m |
| 92 | func (s scale) bodyS() float64 { return 2.2 * s.cqw } // --slides-body-s |
| 93 | func (s scale) code() float64 { return 2.3 * s.cqw } // --slides-code |
| 94 | |
| 95 | // gap is `--slides-gap` as a DESCENDANT reads it: the space between |
| 96 | // two blocks in a region, between two columns of a columns block, and |
| 97 | // the padding of image-full's heading scrim. |
| 98 | func (s scale) gap() float64 { return 2.6 * s.cqw } |
| 99 | |
| 100 | // rule is `--slides-rule`: a divider's thickness and the section |
| 101 | // template's accent bar. |
| 102 | func (s scale) rule() float64 { return 0.5 * s.cqw } |
| 103 | |
| 104 | // bodySize maps a size step to this scale's body sizes. |
| 105 | func (s scale) bodySize(step string) float64 { |
| 106 | switch step { |
| 107 | case "s": |
| 108 | return s.bodyS() |
| 109 | case "l": |
| 110 | return s.bodyL() |
| 111 | } |
| 112 | return s.bodyM() |
| 113 | } |
| 114 | |
| 115 | // Line heights, as the stylesheet sets them: 1.3 on the section and |
| 116 | // inherited by everything, 1.1 on a heading, 1.45 in a code block. |
| 117 | // |
| 118 | // These are ratios of the font size, which is what CSS `line-height: N` |
| 119 | // means, and they are the reason this package does not use the faces' |
| 120 | // own default leading (fonts.Metrics returns it and this ignores it): |
| 121 | // the browser is following the stylesheet, so agreeing with the browser |
| 122 | // means following the stylesheet too. |
| 123 | const ( |
| 124 | leadBody = 1.3 |
| 125 | leadHeading = 1.1 |
| 126 | leadCode = 1.45 |
| 127 | ) |
| 128 | |
| 129 | // trackHeading is `letter-spacing`, in ems, and a heading is the only |
| 130 | // place the stylesheet sets it. Small, and not negligible: over a |
| 131 | // thirty-character title it is nearly a word's width. |
| 132 | const trackHeading = -0.01 |
| 133 | |
| 134 | // Gaps inside a block, all of them relative to the block's own font |
| 135 | // size because that is how the stylesheet writes them. |
| 136 | const ( |
| 137 | paraGapEm = 0.6 // `[slides-block] p + p { margin-block-start: 0.6em }` |
| 138 | listGapEm = 0.4 // `[data-block="list"] { gap: 0.4em }` |
| 139 | quoteGap = 0.6 // `[data-block="quote"] { gap: 0.6em }` |
| 140 | citeEm = 0.62 // `figcaption { font-size: 0.62em }` |
| 141 | inlineEm = 0.88 // `:not(pre) > code { font-size: 0.88em }` |
| 142 | ) |
| 143 | |
| 144 | // listIndentEm is `padding-inline-start` per indent step (§2.6: a |
| 145 | // nested list is a second list block with a bigger indent, never a |
| 146 | // nested <ul>). Four entries, because the stylesheet has four rules. |
| 147 | var listIndentEm = [4]float64{1.4, 3.0, 4.6, 6.2} |
| 148 | |
| 149 | // Code block geometry: `padding: 1em; border-radius: 0.6em`. |
| 150 | const ( |
| 151 | codePadEm = 1.0 |
| 152 | codeRadiusEm = 0.6 |
| 153 | ) |
| 154 | |
| 155 | // Table geometry: `padding: 0.45em 0.7em` on every cell and a 1px rule |
| 156 | // under every row. |
| 157 | const ( |
| 158 | cellPadYEm = 0.45 |
| 159 | cellPadXEm = 0.7 |
| 160 | cellRule = 1.0 |
| 161 | ) |
| 162 | |
| 163 | // shapeMinBlockCqw is `[data-block="shape"] { min-block-size: 8cqw }`, |
| 164 | // which is the floor a shape shrinks to and no further. |
| 165 | const shapeMinBlockCqw = 8.0 |
| 166 | |
| 167 | // The mixes the stylesheet makes with color-mix(), as fractions of the |
| 168 | // foreground mixed into the background. |
| 169 | const ( |
| 170 | mixCodePanel = 0.08 // a code block's panel |
| 171 | mixTableRule = 0.22 // a table's row rule |
| 172 | mixDivider = 0.30 // a divider |
| 173 | mixHighlight = 0.26 // a <mark>, mixed from the ACCENT not the fg |
| 174 | |
| 175 | // imageFallback is the ratio an image with no registered size is |
| 176 | // drawn at — the frame's own, which is the least surprising guess. |
| 177 | imageFallback = 9.0 / 16.0 |
| 178 | ) |
| 179 | |