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