]> git.draconx.ca Git - homepage.git/blob - content/style.scss
Improve clicky table generation.
[homepage.git] / content / style.scss
1 /*
2  * Nick's web site: default stylesheet
3  *
4  * Copyright © 2018-2021 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 // colour definitions
21 $backgroundcolour:  #ffffff;
22 $foregroundcolour:  #000000;
23
24 $linkdefaultcolour: #0000cd;
25 $linkactivecolour:  #ff0000;
26 $linkvisitedcolour: #800080;
27
28 $ruledefaultcolour: #d3d3d3;
29 $rulestrongcolour:  #696969;
30
31 $annotationcolour:  #708090;
32
33 $tableshadecolour:  #f5f5f5;
34
35 @mixin header_size($maxwidth, $fontsize) {
36     font-size: $fontsize;
37     max-width: 1em * ($maxwidth / $fontsize);
38 }
39
40 body {
41     background-color: $backgroundcolour;
42     color: $foregroundcolour;
43     font-family: sans-serif;
44     margin: 1em;
45 }
46
47 a:link { color: $linkdefaultcolour; }
48 a:visited { color: $linkvisitedcolour; }
49 a:active { color: $linkactivecolour; }
50
51 h1 { @include header_size(60em, 2em); }
52 h2 { @include header_size(60em, 1.5em); }
53 h5 { @include header_size(60em, 1em); }
54
55 p>img { max-width: 40em; width: 100%; height: auto; }
56
57 p, dt, dd, li {
58     text-align: justify;
59     @media (max-width: 28em) { text-align: left; }
60     padding: 0;
61     margin: 0;
62 }
63
64 p, table, div, ul, ol, dl, hr {
65     max-width: 50em;
66     padding: 0;
67     margin: 0;
68 }
69
70 p, table, body>div, h5 { margin: 1em 0; }
71
72 li { margin: 0 0 0 2em; }
73 dd { margin: 0 0 0 1em; }
74
75 hr {
76     clear: both;
77     margin: 0.5em 0;
78     border: 0;
79     border-top: 1px solid $ruledefaultcolour;
80 }
81
82 kbd {
83     font-family: monospace;
84     font-size: 0.95em;
85     &:before { content: "% "; }
86 }
87
88 .permalink {
89     font-size: small;
90     color: $annotationcolour;
91
92     a:link, a:visited { color: inherit; }
93     @media not screen { visibility: hidden; }
94 }
95
96 // General table styles.
97 table {
98     border: 1px solid $ruledefaultcolour;
99     border-collapse: collapse;
100     width: 100%;
101 }
102
103 table>* { font-size: 0.9em; }
104 caption {
105     caption-side: top;
106     font-weight: bold;
107     font-size: 1em;
108     text-align: left;
109     margin: 0 0 0.5em 0;
110 }
111
112 td, th {
113     vertical-align: middle;
114     text-align: left;
115     padding: 1ex;
116     margin: 0;
117 }
118
119 thead>tr, tbody>tr { border: solid $ruledefaultcolour; }
120 th, thead>tr { border-bottom: 1px solid $rulestrongcolour; }
121 tbody+tbody { border-bottom: 1px solid $ruledefaultcolour; }
122 *>table, *>th { border: none; }
123 thead>tr { border-width: 1px; }
124 tbody>tr { border-width: 0 1px; }
125
126 td + td { box-shadow: -1px 0 $backgroundcolour; }
127
128 tbody>tr {
129     &:nth-of-type(even) { background-color: $tableshadecolour; }
130     &:last-child { border-bottom: solid 1px $ruledefaultcolour; }
131 }
132
133 // Specific table styles
134 table.cc {
135     &>tr>*:first-child, &>*>tr>*:first-child {
136         &+* { text-align: center; }
137         text-align: center;
138     }
139 }
140
141 // CSS rules for stortable clicky table headers: Update the display of
142 // the /table based on the current state.  Each column has its own set
143 // nearly-identical rules, only the class names differ.
144 //
145 // The clickytables.xsl stylesheet generates two inputs for each column.
146 // These inputs are siblings of the table and all precede it in document
147 // order.  Moreover, the inputs for a column are ordered with respect to
148 // each other, in this sequence:
149 //
150 //   input.clicky-NAME     -- checked iff NAME is selected for sorting.
151 //   input.clicky-NAME-rev -- checked to select reverse order.
152 //
153 // One of the column selection inputs will have a 'checked' attribute to
154 // indicate the default order.  This input is always first in document
155 // order.  No other inputs begin checked.
156 //
157 // The table itself consists of a thead (where the header labels are
158 // located) and two tbody elements.  The bulk of these rules relate
159 // to updating the headers to visually indicate the current state.
160 //
161 // A sortable column's th element has the .clicky-NAME class, matching
162 // its corresponding inputs, and has two label children.  The first label
163 // is visible only when the column is unselected, and is linked to the
164 // .clicky-NAME input to activate that column.  The second label is visible
165 // only on the selected column and is linked to the .clicky-NAME-rev input
166 // to toggle the reverse order.
167 //
168 // For the table body, the first tbody contains the default ordering
169 // and is not styled by these rules (except to hide it when alternate
170 // orderings are selected).  The second tbody contains rows for all
171 // alternate orderings, and is revealed by these rules.  When revealed,
172 // rows with the NAMEfwd or NAMErev class (for the forward and reverse
173 // orderings, respectively) are shown and other rows are hidden.
174
175 $clickynames: name, date, size;
176 @each $col in $clickynames {
177     input.clicky-#{$col} {
178         &:checked {
179             &~table {
180                 // Update table header state
181                 & th.clicky-#{$col} {
182                     label~label {
183                         display: -moz-inline-box !important;
184                         display: inline-block !important;
185                     }
186                     label { display: none; }
187                 }
188
189                 // Show only appropriate items from the sort body (forward)
190                 &>tbody+tbody>tr.#{$col}fwd { display: table-row; }
191                 &>tbody+tbody>tr { display: none; }
192
193                 // Unhide sort body
194                 &>tbody {
195                     &+tbody { display: table-row-group !important; }
196                     display: none;
197                 }
198             }
199
200             // reverse state for selected sort column
201             &~input.clicky-#{$col}-rev {
202                 &:checked ~ table {
203                     // Show only appropriate items from sort body (reversed)
204                     &>tbody+tbody>tr.#{$col}rev { display: table-row; }
205                     &>tbody+tbody>tr { display: none; }
206
207                     // Unhide sort body
208                     &>tbody {
209                         &+tbody { display: table-row-group !important; }
210                         display: none;
211                     }
212                 }
213
214                 // Unhide to allow keyboard navigation to this input
215                 display: block !important;
216             }
217         }
218
219         // If default input element is the only one selected, match it to
220         // return to default view (overriding the changes above).  It is
221         // always the first input element among all the sibling elements.
222         // This seems to interoperate better than using the [checked] or
223         // :first-of-type selectors.
224         @at-root &:first-child, :not(input)+& {
225             &:checked~table>tbody {
226                 &+tbody { display: none !important; }
227                 display: table-row-group;
228             }
229         }
230
231         &:focus ~ table th.clicky-#{$col}>label~label>span {
232             border-color: $foregroundcolour;
233         }
234
235         // Unhide to allow keyboard navigation
236         display: block !important;
237         pointer-events: none;
238         position: absolute;
239         opacity: 0;
240         z-index: -1;
241     }
242
243     input.clicky-#{$col}-rev {
244         &:checked ~ table {
245             // Update table header state
246             & th.clicky-#{$col} {
247                 img+img {
248                     display: -moz-inline-box !important;
249                     display: inline-block !important;
250                 }
251                 img { display: none; }
252             }
253         }
254
255         &:focus ~ table th.clicky-#{$col}>label~label>img {
256             border-color: $foregroundcolour;
257         }
258
259         pointer-events: none;
260         position: absolute;
261         opacity: 0;
262         z-index: -2;
263     }
264
265     th.clicky-#{$col}>label {
266         &, &>* {
267             white-space: nowrap;
268             vertical-align: middle;
269             display: -moz-inline-box;
270             display: inline-block;
271             cursor: pointer;
272         }
273
274         &>* { border: 1px dotted transparent; }
275
276         // Expand the first label a bit so the table (hopefully)
277         // does not reshape as columns are selected.
278         &:first-child {
279             margin-right: 1.75em;
280             padding-right: 2px;
281         }
282
283         &:active { color: $linkactivecolour; }
284         &:first-child:active>span, &~label:active>img {
285             border-color: $linkactivecolour;
286         }
287
288         img {
289             margin-left: 0.25em;
290             width: 1.5em;
291             height: auto;
292         }
293     }
294 }
295
296 table.filelist {
297     &>tr>*:first-child, &>*>tr>*:first-child {
298         &+td { min-width: 50%; }
299         width: 0;
300     }
301
302     tbody img {
303         display: block;
304         height: 1.5em;
305         width: auto;
306     }
307 }
308
309 // Site header rules
310 #breadcrumbs>*, #sitetitle>* { font-size: 0.8em; }
311 #breadcrumbs {
312     * {
313         display: inline;
314         list-style-type: none;
315         vertical-align: top;
316         padding: 0;
317         margin: 0;
318     }
319
320     li + li:before { content: "/ "; }
321 }
322 #sitetitle * {
323     display: inline-block;
324     float: right;
325 }
326
327 // Site footer rules
328 #footer, #article-info {
329     text-align: center;
330     max-width: 44em;
331     padding: 0 3em;
332     margin: 0;
333
334     p {
335         display: inline-block;
336         font-size: 0.8em;
337         max-width: 100%;
338         margin: 0.2em 0;
339     }
340 }
341
342 #footer p { color: $annotationcolour; }
343 #article-info p { font-style: italic; }
344
345 // "unordered" lists with explicit ordering in content
346 ul.ordered > {
347     li { list-style: none; }
348     li>span:first-child, li>*:first-child>span:first-child {
349         display: inline-block;
350         text-align: right;
351         margin-left: -1.8em;
352         min-width: 1.8em;
353     }
354 }
355
356 @media (max-width: 512px) {
357     body { margin: 0.6em; }
358     ul ul { margin-left: -1.2em; }
359     dd { margin: 0; }
360 }
361
362 @media (max-width: 35em) {
363     #sitetitle * { float: none; }
364     #footer { padding: 0 1em; }
365 }