Skip to content

Commit 8c0958c

Browse files
Add a dark mode
1 parent d84c6df commit 8c0958c

20 files changed

Lines changed: 924 additions & 67 deletions

File tree

‎codespeed/static/css/dygraph.css‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
top: 0px;
1414
right: 2px;
1515
*/
16-
background: white;
16+
background: var(--surface, white);
1717
line-height: normal;
1818
text-align: left;
1919
overflow: hidden;
@@ -63,8 +63,8 @@
6363
/* This class only applies to annotations without icons */
6464
/* Old class name: .dygraphDefaultAnnotation */
6565
.dygraph-default-annotation {
66-
border: 1px solid black;
67-
background-color: white;
66+
border: 1px solid var(--text, black);
67+
background-color: var(--surface, white);
6868
text-align: center;
6969
}
7070

@@ -74,7 +74,7 @@
7474
z-index: 10;
7575
line-height: normal;
7676
overflow: hidden;
77-
color: black; /* replaces old axisLabelColor option */
77+
color: var(--chart-text, black); /* replaces old axisLabelColor option */
7878
}
7979

8080
.dygraph-axis-label-x {

‎codespeed/static/css/main.css‎

Lines changed: 139 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
* Visual values live in the custom properties below; change the look there.
77
*/
88

9+
/* Light theme. */
910
:root {
11+
color-scheme: light;
12+
1013
--font-sans: "Fira Sans", Helvetica, Arial, sans-serif;
1114
--font-mono: "Menlo", "ProFont", "Inconsolata", "Lucida Sans Console", monospace;
1215

@@ -23,7 +26,9 @@
2326
--accent: #2563eb;
2427
--accent-hover: #1d4ed8;
2528
--accent-soft: #e8efff;
29+
--warning: #d9480f;
2630

31+
/* Changes table cells: background and the text on it */
2732
--status-red: #FF5640;
2833
--status-red-text: #1f2328;
2934
--status-green: #9FD54D;
@@ -32,14 +37,69 @@
3237
--status-lightgreen-text: #1f2328;
3338
--status-yellow: #FFD843;
3439
--status-yellow-text: #1f2328;
40+
/* Home page report summaries: coloured text on the card */
41+
--summary-red: #FF5640;
42+
--summary-green: #5c9a1b;
43+
--summary-lightgreen: #8bb000;
44+
--summary-yellow: #c9a000;
45+
46+
/* Read by the chart scripts (Theme.chartColors in codespeed.js) for the
47+
parts of a canvas chart the stylesheet cannot reach */
48+
--chart-text: #636c76;
49+
--chart-grid: rgba(0, 0, 0, 0.1);
3550

3651
--radius: 8px;
3752
--radius-sm: 4px;
3853
--shadow: 0 1px 2px rgba(16, 24, 40, 0.06), 0 1px 3px rgba(16, 24, 40, 0.08);
54+
--shadow-raised: 0 6px 16px rgba(16, 24, 40, 0.12);
55+
--tooltip-bg: rgba(255, 255, 255, 0.96);
56+
/* The home page thumbnails are screenshots of the light theme */
57+
--thumb-filter: none;
3958
--sidebar-width: 14em;
4059
--gap: 1em;
4160
}
4261

62+
/* Dark theme. */
63+
:root[data-theme="dark"] {
64+
color-scheme: dark;
65+
66+
--page-bg: #0d1117;
67+
--surface: #161b22;
68+
--surface-alt: #1f242c;
69+
--border: #30363d;
70+
--border-strong: #484f58;
71+
72+
--text: #e6edf3;
73+
--text-muted: #9ea7b3;
74+
--text-faint: #7d8590;
75+
76+
--accent: #58a6ff;
77+
--accent-hover: #79c0ff;
78+
--accent-soft: #1b2d4a;
79+
--warning: #f0883e;
80+
81+
--status-red: #9f2f24;
82+
--status-red-text: #fbe3e0;
83+
--status-green: #3b7a28;
84+
--status-green-text: #e6f4de;
85+
--status-lightgreen: #5f8a1e;
86+
--status-lightgreen-text: #eef7da;
87+
--status-yellow: #96760e;
88+
--status-yellow-text: #fcf2d0;
89+
--summary-red: #ff7b72;
90+
--summary-green: #7ccf5a;
91+
--summary-lightgreen: #b8e04c;
92+
--summary-yellow: #e3b341;
93+
94+
--chart-text: #9ea7b3;
95+
--chart-grid: rgba(255, 255, 255, 0.12);
96+
97+
--shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 1px 3px rgba(0, 0, 0, 0.5);
98+
--shadow-raised: 0 6px 16px rgba(0, 0, 0, 0.55);
99+
--tooltip-bg: rgba(22, 27, 34, 0.97);
100+
--thumb-filter: invert(0.88) hue-rotate(180deg);
101+
}
102+
43103
/* Global styles */
44104
html { box-sizing: border-box; }
45105
*, *::before, *::after { box-sizing: inherit; }
@@ -110,6 +170,34 @@ ul#links li a:hover, ul#links li a:focus {
110170
text-decoration: none;
111171
}
112172

173+
/* Theme toggle: shows the icon of the theme a click switches to */
174+
button#theme-toggle {
175+
display: inline-flex;
176+
align-items: center;
177+
justify-content: center;
178+
width: 2em;
179+
height: 2em;
180+
margin-left: 0.25em;
181+
padding: 0;
182+
color: var(--text-muted);
183+
background: none;
184+
border: 0;
185+
border-radius: var(--radius-sm);
186+
cursor: pointer;
187+
}
188+
button#theme-toggle:hover, button#theme-toggle:focus-visible {
189+
color: var(--text);
190+
background-color: var(--surface-alt);
191+
}
192+
button#theme-toggle svg { width: 1.1em; height: 1.1em; }
193+
button#theme-toggle .icon-sun,
194+
:root[data-theme="dark"] button#theme-toggle .icon-moon { display: none; }
195+
:root[data-theme="dark"] button#theme-toggle .icon-sun { display: block; }
196+
197+
/* A site can ship a second logo for the dark theme (see base_site.html) */
198+
:root[data-theme="dark"] div#title img.logo-light,
199+
:root:not([data-theme="dark"]) div#title img.logo-dark { display: none; }
200+
113201
div#wrapper { margin: 0; }
114202

115203
/* Section tabs */
@@ -189,25 +277,36 @@ div#presentation div.menubox {
189277
}
190278
div#presentation div.menubox:hover {
191279
border-color: var(--accent);
192-
box-shadow: 0 6px 16px rgba(16, 24, 40, 0.12);
280+
box-shadow: var(--shadow-raised);
193281
transform: translateY(-2px);
194282
}
195283
div#presentation div.menubox h2 { font-size: 1.1em; font-weight: 600; }
196284
div#presentation div.menubox p { color: var(--text-muted); }
197285

198-
/* The three icons differ in size (98 to 147px wide); scale them to one height */
286+
/* The thumbnail sits in a pseudo-element so the dark theme can filter it
287+
without touching the text */
199288
div#presentation div.menubox p {
289+
position: relative;
200290
min-height: 4.6em;
201291
margin: 0.6em 0 0;
202292
padding-left: 122px;
203293
text-align: left;
294+
}
295+
div#presentation div.menubox p::before {
296+
content: '';
297+
position: absolute;
298+
top: 0.2em;
299+
left: 0;
300+
width: 122px;
301+
height: 65px;
204302
background-repeat: no-repeat;
205-
background-position: 0 0.2em;
303+
background-position: 0 0;
206304
background-size: auto 65px;
305+
filter: var(--thumb-filter);
207306
}
208-
div#presentation div#changes p { background-image: url(../images/changes.png); }
209-
div#presentation div#timeline p { background-image: url(../images/timeline.png); }
210-
div#presentation div#comparison p { background-image: url(../images/comparison.png); }
307+
div#presentation div#changes p::before { background-image: url(../images/changes.png); }
308+
div#presentation div#timeline p::before { background-image: url(../images/timeline.png); }
309+
div#presentation div#comparison p::before { background-image: url(../images/comparison.png); }
211310

212311

213312
/* Sidebar and content columns */
@@ -364,11 +463,20 @@ table.tablesorter thead tr th {
364463
border-right: 1px solid var(--border);
365464
}
366465
table.tablesorter thead tr th:last-child { border-right: 0; }
367-
table.tablesorter thead tr th.header {
368-
background-image: url(../images/bg.gif);
369-
background-repeat: no-repeat;
370-
background-position: center right;
371-
cursor: pointer;
466+
table.tablesorter thead tr th.header { position: relative; cursor: pointer; }
467+
/* Sort indicator: both arrows on a sortable column, one on the sorted column.
468+
Drawn through a mask so it takes the theme's colours. */
469+
table.tablesorter thead tr th.header::after {
470+
content: '';
471+
position: absolute;
472+
top: 50%;
473+
right: 4px;
474+
width: 8px;
475+
height: 10px;
476+
margin-top: -5px;
477+
background-color: var(--text-faint);
478+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 10'%3E%3Cpath d='M4 0 8 4H0zM0 6h8l-4 4z'/%3E%3C/svg%3E") no-repeat center / contain;
479+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 10'%3E%3Cpath d='M4 0 8 4H0zM0 6h8l-4 4z'/%3E%3C/svg%3E") no-repeat center / contain;
372480
}
373481
table.tablesorter tfoot tr td {
374482
padding: 0.4em 0.5em;
@@ -390,12 +498,20 @@ table.tablesorter tbody td {
390498
}
391499
table.tablesorter tbody td:last-child, table.tablesorter tfoot td:last-child { border-right: 0; }
392500
table.tablesorter tbody tr.odd td { background-color: var(--surface-alt); }
393-
table.tablesorter thead tr .headerSortUp { background-image: url(../images/asc.gif); }
394-
table.tablesorter thead tr .headerSortDown { background-image: url(../images/desc.gif); }
501+
table.tablesorter thead tr .headerSortUp::after {
502+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 10'%3E%3Cpath d='M4 2.5 8 6.5H0z'/%3E%3C/svg%3E");
503+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 10'%3E%3Cpath d='M4 2.5 8 6.5H0z'/%3E%3C/svg%3E");
504+
}
505+
table.tablesorter thead tr .headerSortDown::after {
506+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 10'%3E%3Cpath d='M0 3.5h8l-4 4z'/%3E%3C/svg%3E");
507+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 10'%3E%3Cpath d='M0 3.5h8l-4 4z'/%3E%3C/svg%3E");
508+
}
395509
table.tablesorter thead tr .headerSortDown, table.tablesorter thead tr .headerSortUp {
396510
background-color: var(--accent-soft);
397511
color: var(--accent);
398512
}
513+
table.tablesorter thead tr .headerSortDown::after,
514+
table.tablesorter thead tr .headerSortUp::after { background-color: var(--accent); }
399515
table.tablesorter tbody td:not(:first-child),
400516
table.tablesorter tfoot td:not(:first-child) {
401517
font-family: var(--font-mono);
@@ -406,16 +522,16 @@ table.tablesorter tfoot td:first-child { text-align: left; }
406522

407523
table.tablesorter tbody tr td.status-red, td.status-red {
408524
background-color: var(--status-red); color: var(--status-red-text); }
409-
.reports tr.status-red td.summary { color: var(--status-red); font-weight: 600; }
525+
.reports tr.status-red td.summary { color: var(--summary-red); font-weight: 600; }
410526
table.tablesorter tbody tr td.status-green, td.status-green {
411527
background-color: var(--status-green); color: var(--status-green-text); }
412-
tr.status-green td.summary { color: #5c9a1b; font-weight: 600; }
528+
tr.status-green td.summary { color: var(--summary-green); font-weight: 600; }
413529
table.tablesorter tbody tr td.status-yellow, td.status-yellow {
414530
background-color: var(--status-yellow); color: var(--status-yellow-text); }
415-
tr.status-yellow td.summary { color: #c9a000; font-weight: 600; }
531+
tr.status-yellow td.summary { color: var(--summary-yellow); font-weight: 600; }
416532
table.tablesorter tbody tr td.status-lightgreen, td.status-lightgreen {
417533
background-color: var(--status-lightgreen); color: var(--status-lightgreen-text); }
418-
tr.status-lightgreen td.summary { color: #8bb000; font-weight: 600; }
534+
tr.status-lightgreen td.summary { color: var(--summary-lightgreen); font-weight: 600; }
419535

420536
table.tablesorter tbody tr.highlight td, tr.highlight td {
421537
background-color: var(--accent-soft) !important;
@@ -468,10 +584,8 @@ a#permalink, a#exportcsv {
468584
gap: 0.25em;
469585
font-size: 0.9em;
470586
}
587+
/* The download glyph's viewBox carries a margin, so both look the same size */
471588
a#permalink svg, a#exportcsv svg { width: 1.1em; height: 1.1em; }
472-
/* The download glyph fills its 16px grid, the link glyph does not;
473-
shrink it slightly so the two look the same size. */
474-
a#exportcsv svg { width: 0.95em; height: 0.95em; }
475589
a#exportcsv + a#permalink { margin-left: 1em; }
476590

477591
/* Plot styles */
@@ -493,7 +607,7 @@ div.miniplot:hover { border-color: var(--accent); background-color: var(--surfac
493607
#dygraph-tooltip {
494608
display: none;
495609
position: absolute;
496-
background: rgba(255, 255, 255, 0.96);
610+
background: var(--tooltip-bg);
497611
color: var(--text);
498612
border: 1px solid var(--border-strong);
499613
border-radius: var(--radius-sm);
@@ -502,7 +616,7 @@ div.miniplot:hover { border-color: var(--accent); background-color: var(--surfac
502616
pointer-events: none;
503617
z-index: 100;
504618
white-space: nowrap;
505-
box-shadow: 0 4px 12px rgba(16, 24, 40, 0.14);
619+
box-shadow: var(--shadow-raised);
506620
}
507621

508622
div.compplot {
@@ -536,8 +650,8 @@ p.note, p.warning {
536650
border: 1px solid var(--border);
537651
border-radius: var(--radius-sm);
538652
}
539-
p.warning { margin-left: 10px; border-left: 3px solid #d9480f; }
540-
p.warning::before { content: '⚠ '; color: #d9480f; }
653+
p.warning { margin-left: 10px; border-left: 3px solid var(--warning); }
654+
p.warning::before { content: '⚠ '; color: var(--warning); }
541655
p.note {
542656
margin: 20px 2.4em 0;
543657
padding-left: 55px;
@@ -591,5 +705,5 @@ div.footer a:hover { color: var(--accent); text-decoration: underline; }
591705
}
592706

593707
/* speed.pypy.org additions */
594-
.envlinecolor { float: right; font-size: 12px; line-height: 18px; color: #555; }
708+
.envlinecolor { float: right; font-size: 12px; line-height: 18px; color: var(--text-muted); }
595709
a#exportcsv:hover { text-decoration: underline; }

‎codespeed/static/images/asc.gif‎

-54 Bytes
Binary file not shown.

‎codespeed/static/images/bg.gif‎

-64 Bytes
Binary file not shown.

‎codespeed/static/images/desc.gif‎

-54 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)