]> git.draconx.ca Git - homepage.git/blob - content/style.scss
cdecl99-1.3 bash-5 hotfix
[homepage.git] / content / style.scss
1 /*!
2  * Nick's web site: default stylesheet
3  *
4  * Copyright © 2018-2022 Nick Bowler
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19
20 @import "lib/colourmap.scss";
21
22 @include defcolours
23     ( $background:  #ffffff #000000
24     , $foreground:  #000000 #ffffff
25
26     , $linkdefault: #0000cd #a3aaff
27     , $linkactive:  #ff0000
28     , $linkvisited: #800080 #e493f7
29     , $focusring:   #628cb2
30
31     , $annotation:  #708090
32     , $tableshade:  #f5f5f5 #101010
33
34     , $ruledefault: #d3d3d3 #494949
35     , $rulestrong:  #696969 #939393
36     );
37
38 @mixin header_size($maxwidth, $fontsize) {
39     font-size: $fontsize;
40     max-width: 1em * ($maxwidth / $fontsize);
41 }
42
43 html { @include usecolours($background-color: background); }
44 body {
45     @include usecolours($color: foreground);
46     font-family: sans-serif;
47     margin: 1em;
48 }
49
50 a:link {
51     @include usecolours($color: linkdefault, $border-color: linkdefault);
52 }
53 a:visited {
54     @include usecolours($color: linkvisited, $border-color: linkvisited);
55 }
56 a:active {
57     @include usecolours($color: linkactive, $border-color: linkactive);
58 }
59
60 @supports (outline-style: auto) {
61     a:link { border-width: 0; }
62     a:focus { @include usecolour(outline, focusring, auto); }
63     li, td, dt { &>a:link { border: solid 1px transparent; } }
64 }
65
66 h1 { @include header_size(60em, 2em); }
67 h2 { @include header_size(60em, 1.5em); }
68 h5 { @include header_size(60em, 1em); }
69
70 h1, h2 { abbr { text-decoration: none; } }
71
72 @supports (display: grid) {
73     .gallery {
74         display: grid;
75         grid-column-gap: 1em;
76         grid-template-columns: repeat( auto-fill, minmax(18em, 1fr) );
77         align-items: center;
78
79         p.img { margin: 0.5em 0; }
80     }
81
82     @media (max-width: 45em) {
83         .inline.gallery {
84             display: block;
85             p.img {
86                 a { max-width: 24em; }
87                 margin: 1em 0;
88             }
89         }
90     }
91 }
92
93 p.img {
94     text-align: center;
95
96     img {
97         vertical-align: bottom;
98         max-width: 40em;
99         width: 100%;
100         height: auto;
101     }
102
103     a {
104         text-decoration: none;
105         display: inline-block;
106         border: solid 2px;
107     }
108
109     a.left { margin: 0 1em 0.5em 0; float: left; clear: left; }
110     a.right { margin: 0 0 0.5em 1em; float: right; clear: right; }
111     a.left, a.right {
112         @media (max-width: 45em) { float: none; margin: 0; }
113
114         max-width: 24em;
115     }
116
117     small {
118         @include usecolours($color: foreground);
119         @media (max-width: 24em) { text-align: left; }
120         padding: 0.5ex;
121         display: block;
122         display: -moz-inline-box;
123         display: inline-block;
124         text-align: justify;
125         font-size: 0.9em;
126     }
127 }
128
129 p, dt, dd, li {
130     text-align: justify;
131     @media (max-width: 28em) { text-align: left; }
132     padding: 0;
133     margin: 0;
134 }
135
136 p, table, div, ul, ol, dl, hr {
137     max-width: 50em;
138     padding: 0;
139     margin: 0;
140 }
141
142 p, table, body>div, h5 { margin: 1em 0; }
143 blockquote {
144     @media (max-width: 28em) { margin: 1em 0.5em; }
145     margin: 1em;
146 }
147
148 li { margin: 0 0 0 2em; }
149 dd { margin: 0 0 0 1em; }
150
151 hr {
152     clear: both;
153     margin: 0.5em 0;
154     border: 0;
155     @include usecolour(border-top, ruledefault, 1px solid);
156 }
157
158 kbd {
159     font-family: monospace;
160     font-size: 0.95em;
161     &:before { content: "% "; }
162     &.ok:before { content: "ok "; }
163     &>span { white-space: nowrap; }
164
165     blockquote & {
166         display: block;
167         & + br { display: none; }
168         text-align: left;
169         padding-left: 3em;
170         text-indent: -3em;
171     }
172 }
173
174 .permalink {
175     @include usecolours($color: annotation);
176     font-size: small;
177
178     a:link, a:visited { color: inherit; }
179     a:active { @include usecolours($color: linkactive); }
180     @media not screen { visibility: hidden; }
181 }
182
183 // General table styles.
184 table {
185     @include usecolour(border-top, ruledefault, 1px solid);
186     border-collapse: collapse;
187     clear: both;
188     width: 100%;
189 }
190
191 table>* { font-size: 0.9em; }
192 caption {
193     caption-side: top;
194     font-weight: bold;
195     font-size: 1em;
196     text-align: left;
197     margin: 0 0 0.5em 0;
198 }
199
200 td, th {
201     vertical-align: middle;
202     text-align: left;
203     padding: 1ex;
204     margin: 0;
205 }
206
207 thead>tr, tbody>tr { @include usecolour(border, ruledefault, solid); }
208 th, thead>tr { @include usecolour(border-bottom, rulestrong, 1px solid); }
209 tbody+tbody { @include usecolour(border-bottom, ruledefault, 1px solid); }
210 *>table, *>th { border: none; }
211 thead>tr { border-width: 1px; }
212 tbody>tr { border-width: 0 1px; }
213
214 td + td {
215     @include usecolour(box-shadow, background, -1px 0);
216 }
217
218 tbody>tr {
219     &:nth-of-type(even) { @include usecolours($background-color: tableshade); }
220     &:last-child { @include usecolour(border-bottom, ruledefault, solid 1px); }
221 }
222
223 // Specific table styles
224 table.cc {
225     &>tr>*:first-child, &>*>tr>*:first-child {
226         &+* { text-align: center; }
227         text-align: center;
228     }
229 }
230
231 // CSS rules for sortable clicky table headers: Update the display of
232 // the table based on the current state.  Each column has its own set
233 // nearly-identical rules, only the class names differ.
234 //
235 // The clickytables.xsl stylesheet generates two inputs for each column.
236 // These inputs are siblings of the table and all precede it in document
237 // order.  Moreover, the inputs for a column are ordered with respect to
238 // each other, in this sequence:
239 //
240 //   input.clicky-NAME     -- checked iff NAME is selected for sorting.
241 //   input.clicky-NAME-rev -- checked to select reverse order.
242 //
243 // One of the column selection inputs will have a 'checked' attribute to
244 // indicate the default order.  This input is always first in document
245 // order.  No other inputs begin checked.
246 //
247 // The table itself consists of a thead (where the header labels are
248 // located) and two tbody elements.  The bulk of these rules relate
249 // to updating the headers to visually indicate the current state.
250 //
251 // A sortable column's th element has the .clicky-NAME class, matching
252 // its corresponding inputs, and has two label children.  The first label
253 // is visible only when the column is unselected, and is linked to the
254 // .clicky-NAME input to activate that column.  The second label is visible
255 // only on the selected column and is linked to the .clicky-NAME-rev input
256 // to toggle the reverse order.
257 //
258 // For the table body, the first tbody contains the default ordering
259 // and is not styled by these rules (except to hide it when alternate
260 // orderings are selected).  The second tbody contains rows for all
261 // alternate orderings, and is revealed by these rules.  When revealed,
262 // rows with the NAMEfwd or NAMErev class (for the forward and reverse
263 // orderings, respectively) are shown and other rows are hidden.
264
265 $clickynames: name, date, size;
266 @each $col in $clickynames {
267     input.clicky-#{$col} {
268         &:checked {
269             &~table {
270                 // Update table header state
271                 & th.clicky-#{$col} {
272                     label~label {
273                         display: -moz-inline-box !important;
274                         display: inline-block !important;
275                     }
276                     label { display: none; }
277                 }
278
279                 // Show only appropriate items from the sort body (forward)
280                 &>tbody+tbody>tr.#{$col}fwd { display: table-row; }
281                 &>tbody+tbody>tr { display: none; }
282
283                 // Unhide sort body
284                 &>tbody {
285                     &+tbody { display: table-row-group !important; }
286                     display: none;
287                 }
288             }
289
290             // reverse state for selected sort column
291             &~input.clicky-#{$col}-rev {
292                 &:checked ~ table {
293                     // Show only appropriate items from sort body (reversed)
294                     &>tbody+tbody>tr.#{$col}rev { display: table-row; }
295                     &>tbody+tbody>tr { display: none; }
296
297                     // Unhide sort body
298                     &>tbody {
299                         &+tbody { display: table-row-group !important; }
300                         display: none;
301                     }
302                 }
303
304                 // Unhide to allow keyboard navigation to this input
305                 display: block !important;
306             }
307         }
308
309         // If default input element is the only one selected, match it to
310         // return to default view (overriding the changes above).  It is
311         // always the first input element among all the sibling elements.
312         // This seems to interoperate better than using the [checked] or
313         // :first-of-type selectors.
314         @at-root &:first-child, :not(input)+& {
315             &:checked~table>tbody {
316                 &+tbody { display: none !important; }
317                 display: table-row-group;
318             }
319         }
320
321         // Unhide to allow keyboard navigation
322         display: block !important;
323         pointer-events: none;
324         position: absolute;
325         opacity: 0;
326         z-index: -1;
327     }
328
329     input.clicky-#{$col}-rev {
330         &:checked ~ table {
331             // Update table header state
332             & th.clicky-#{$col} {
333                 .svg+.svg {
334                     display: -moz-inline-box !important;
335                     display: inline-block !important;
336                 }
337                 .svg { display: none; }
338             }
339         }
340
341         pointer-events: none;
342         position: absolute;
343         opacity: 0;
344         z-index: -2;
345     }
346
347     $focuslabel: ":focus ~ table th.clicky-#{$col}>label~label";
348     #{"input.clicky-#{$col+$focuslabel}"}>span:first-child
349     , #{"input.clicky-#{$col}-rev#{$focuslabel}"} .svg
350     {
351         @include usecolours($border-color: foreground);
352         @at-root { @supports (outline-style: auto) { & {
353             @include usecolour(outline, focusring, auto);
354             border-color: transparent !important;
355         }}}
356     }
357 }
358
359 thead.clicky label {
360     white-space: nowrap;
361     line-height: 1.5em;
362     cursor: pointer;
363
364     &>* {
365         display: -moz-inline-box;
366         display: inline-block;
367         border: 1px dotted transparent;
368         vertical-align: middle;
369     }
370
371     // Expand the first label a bit so the table (hopefully)
372     // does not reshape as columns are selected.
373     &:first-child {
374         margin-right: 1.75em;
375         padding-right: 2px;
376     }
377
378     &:active { @include usecolours($color: linkactive); }
379     &:first-child:active>span, &~label:active>.svg {
380         @include usecolours($border-color: linkactive);
381         @at-root { @supports (outline-style: auto) { & {
382             @include usecolour(outline, focusring, auto);
383             border-color: transparent;
384         }}}
385     }
386
387     .svg {
388         margin-left: 0.25em;
389     }
390
391     .svg, svg, img.svgfallback {
392         height: 1.5em;
393         width: auto;
394     }
395     .svg svg { width: 1.5em; }
396 }
397
398 table.filelist {
399     &>*>tr>*:first-child {
400         &+* { width: 50%; }
401         // chrome doesn't like width: 0 for some reason
402         width: 0.1px;
403     }
404
405     tbody {
406         .svg, svg, img.svgfallback {
407             vertical-align: middle;
408             height: 1.5em;
409             width: auto;
410         }
411
412         .svg {
413             svg { width: 1.5em; }
414             display: inline-block;
415         }
416     }
417 }
418
419 // Site header rules
420 #breadcrumbs>*, #sitetitle>* { font-size: 0.8em; }
421 #breadcrumbs {
422     * {
423         display: inline;
424         list-style-type: none;
425         vertical-align: top;
426         padding: 0;
427         margin: 0;
428     }
429
430     li + li:before { content: "/ "; }
431 }
432 #sitetitle * {
433     display: inline-block;
434     float: right;
435 }
436
437 // Site footer rules
438 #footer, #article-info {
439     text-align: center;
440     max-width: 44em;
441     padding: 0 3em;
442     margin: 0;
443
444     p {
445         display: inline-block;
446         font-size: 0.8em;
447         max-width: 100%;
448         margin: 0.2em 0;
449     }
450 }
451
452 #footer p { @include usecolours($color: annotation); }
453 #article-info p { font-style: italic; }
454
455 .wbr:after { content: "\200b"; }
456
457 // "unordered" lists with explicit ordering in content
458 ul.ordered > {
459     li { list-style: none; }
460     li>span:first-child, li>*:first-child>span:first-child {
461         display: inline-block;
462         text-align: right;
463         margin-left: -1.8em;
464         min-width: 1.8em;
465     }
466 }
467
468 @media (max-width: 512px) {
469     body { margin: 0.6em; }
470     ul ul { margin-left: -1.2em; }
471     dd { margin: 0; }
472 }
473
474 @media (max-width: 35em) {
475     #sitetitle * { float: none; }
476     #footer { padding: 0 1em; }
477 }
478
479 // lighten icon shadows in dark mode
480 @media (prefers-color-scheme: dark) {
481     svg.icons { path.shadow, g.shadow>* { opacity: 0.7; } }
482     svg.icons .shadow>stop { stop-color: #aaa; }
483     svg.return path.shadow { opacity: 0.45; }
484 }
485
486 // page-specific dark mode styles
487 @media (prefers-color-scheme: dark) and (min-width: 35em) {
488     #page_weblog_responsive_tables {
489         @each $tN in t6 t7 t8 {
490             ##{$tN}>tbody>tr.#{$tN}-split {
491                 @include usecolour_dark_(border-bottom, ruledefault);
492
493                 &:nth-of-type(odd) ~ tr:nth-of-type(odd) {
494                     @include usecolour_dark_(background-color, tableshade);
495                 }
496             }
497         }
498     }
499 }