├── .gitignore
├── README.md
├── _config.yml
├── css
└── news.css
├── favicon.ico
├── images
├── darkbluearrow.png
├── darkbluearrow2x.png
├── grayarrow.gif
├── grayarrow2x.gif
├── s.gif
└── y18.gif
├── index.html
├── item.html
├── js
├── dark.js
└── hn.js
└── screenshot.png
/.gitignore:
--------------------------------------------------------------------------------
1 | # Files
2 |
3 | .DS_Store
4 | .ruby-version
5 | npm-debug.log
6 | test.html
7 | test.sass
8 | _config.local.yml
9 |
10 | # Folders
11 |
12 | .idea/
13 | .jekyll-cache
14 | .sass-cache
15 | _gh_pages
16 | _site
17 | dev
18 | node_modules
19 | test/output/
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Hacker News Dark Mode
2 |
3 |
4 |
5 | ## Process
6 |
7 | I took the [original CSS file](https://news.ycombinator.com/news.css) and applied as few as possible changes in order to set up a Dark Mode.
8 | I took the original literal color values (like `#000000`) and used them to set up a list of initial [CSS Variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties):
9 |
10 | ```css
11 | :root {
12 | --black: #000000;
13 | --dark-grey: #222;
14 | --light-grey: #828282;
15 | --lightest-grey: #eee;
16 | --white: #ffffff;
17 | --orange: #ff6600;
18 | --beige: #f6f6ef;
19 | }
20 | ```
21 |
22 | I then set up semantic variables by referencing initial variables:
23 |
24 | ```css
25 | :root {
26 | --page-background: var(--white);
27 | --accent: var(--orange);
28 | --text: var(--light-grey);
29 | --text-strong: var(--black);
30 | --border: var(--dark-grey);
31 | --background: var(--beige);
32 | --input-background: var(--white);
33 | --input-border: var(--lightest-grey);
34 | }
35 | ```
36 |
37 | At this point, the site should look exactly the same.
38 |
39 | For Dark Mode, we could use the CSS media query [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme).
40 | However, the user might prefer Hacker News in light mode, whilst having his OS in Dark Mode.
41 | Since there is no way to setup a color scheme preference per website, we'll be using a `data` attribute on the `` element:
42 |
43 | ```html
44 |
45 | ```
46 |
47 | This also makes it easier me to force the Dark Mode for demonstration purposes.
48 |
49 | This theme attribute can be targeted with CSS:
50 |
51 | ```css
52 | :root[data-theme="dark"] {
53 | --page-background: var(--darkest-blue);
54 | --accent: var(--blue);
55 | --text: var(--light-blue);
56 | --text-strong: var(--lightest-grey);
57 | --border: var(--light-blue);
58 | --background: var(--dark-blue);
59 | --input-border: var(--dark-blue);
60 | --input-background: var(--darkest-blue);
61 | --input-border: var(--accent);
62 | }
63 | ```
64 |
65 | There were also some additional changes I had to make for the Dark Mode to work.
66 |
67 | ## Dark Mode detection
68 |
69 | For demonstration purposes, the Dark Mode is forced here by setting a `data` attribute on the `` element.
70 | Ideally, this preference would be a user one, and the server would return the appropriate HTML.
71 | It is possible to detect the user's OS preference in JS:
72 |
73 | ```js
74 | window.matchMedia("(prefers-color-scheme: dark)").matches; // True if useOS preference is dark
75 | ```
76 |
77 | But swapping the theme client-side causes **flickering** on load, so it should be avoided.
78 | It could however be used to detect a user's OS preference and set a default value server-side.
79 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | permalink: pretty
2 | url: https://jgthms.com/hacker-news-dark-mode
3 | repo: https://github.com/jgthms/hacker-news-dark-mode/
4 |
--------------------------------------------------------------------------------
/css/news.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --blue-h: 227;
3 | --blue-s: 10%;
4 |
5 | --black: #000000;
6 | --dark-grey: #222;
7 | --light-grey: #828282;
8 | --lightest-grey: #eee;
9 | --white: #ffffff;
10 | --orange: #ff6600;
11 | --beige: #f6f6ef;
12 |
13 | --darkest-blue: hsl(var(--blue-h), var(--blue-s), 7%);
14 | --dark-blue:hsl(var(--blue-h), var(--blue-s), 12%);
15 | --blue:hsl(var(--blue-h), var(--blue-s), 20%);
16 | --light-blue:hsl(var(--blue-h), var(--blue-s), 51%);
17 |
18 | --page-background: var(--white);
19 | --accent: var(--orange);
20 | --text: var(--light-grey);
21 | --text-strong: var(--black);
22 | --border: var(--dark-grey);
23 | --background: var(--beige);
24 | --input-background: var(--white);
25 | --input-border: var(--lightest-grey);
26 |
27 | --c5a: #5a5a5a;
28 | --c73: #737373;
29 | --c88: #888888;
30 | --c9c: #9c9c9c;
31 | --cae: #aeaeae;
32 | --cbe: #bebebe;
33 | --cce: #cecece;
34 | --cdd: #dddddd;
35 | }
36 |
37 | :root[data-theme="dark"] {
38 | --page-background: var(--darkest-blue);
39 | --accent: var(--blue);
40 | --text: var(--light-blue);
41 | --text-strong: var(--lightest-grey);
42 | --border: var(--light-blue);
43 | --background: var(--dark-blue);
44 | --input-border: var(--dark-blue);
45 | --input-background: var(--darkest-blue);
46 | --input-border: var(--accent);
47 |
48 | --c5a: hsl(var(--blue-h), var(--blue-s), 61%);
49 | --c73: hsl(var(--blue-h), var(--blue-s), 56%);
50 | --c88: hsl(var(--blue-h), var(--blue-s), 46%);
51 | --c9c: hsl(var(--blue-h), var(--blue-s), 41%);
52 | --cae: hsl(var(--blue-h), var(--blue-s), 36%);
53 | --cbe: hsl(var(--blue-h), var(--blue-s), 31%);
54 | --cce: hsl(var(--blue-h), var(--blue-s), 26%);
55 | --cdd: hsl(var(--blue-h), var(--blue-s), 21%);
56 | }
57 |
58 | :root[data-theme="dark"] input[type="text"],
59 | :root[data-theme="dark"] input[type='number'],
60 | :root[data-theme="dark"] textarea {
61 | background-color: var(--input-background);
62 | border: 1px solid var(--input-border);
63 | }
64 |
65 | html { background-color: var(--page-background);}
66 | body { font-family:Verdana, Geneva, sans-serif; font-size:10pt; color:var(--text); }
67 | td { font-family:Verdana, Geneva, sans-serif; font-size:10pt; color:var(--text); }
68 |
69 | .admin td { font-family:Verdana, Geneva, sans-serif; font-size:8.5pt; color:var(--text-strong); }
70 | .subtext td { font-family:Verdana, Geneva, sans-serif; font-size: 7pt; color:var(--text); }
71 |
72 | input { font-family:monospace; font-size:10pt; }
73 | input[type="submit"] { font-family:Verdana, Geneva, sans-serif; }
74 | textarea { font-family:monospace; font-size:10pt; }
75 |
76 | a:link { color:var(--text-strong); text-decoration:none; }
77 | a:visited { color:var(--text); text-decoration:none; }
78 |
79 | #hnmain { background-color: var(--background);}
80 | #header { background-color: var(--accent);}
81 | #border { background-color: var(--accent);}
82 |
83 | .default { font-family:Verdana, Geneva, sans-serif; font-size: 10pt; color:var(--text); }
84 | .admin { font-family:Verdana, Geneva, sans-serif; font-size:8.5pt; color:var(--text-strong); }
85 | .title { font-family:Verdana, Geneva, sans-serif; font-size: 10pt; color:var(--text); }
86 | .subtext { font-family:Verdana, Geneva, sans-serif; font-size: 7pt; color:var(--text); }
87 | .yclinks { font-family:Verdana, Geneva, sans-serif; font-size: 8pt; color:var(--text); }
88 | .pagetop { font-family:Verdana, Geneva, sans-serif; font-size: 10pt; color:var(--border); }
89 | .comhead { font-family:Verdana, Geneva, sans-serif; font-size: 8pt; color:var(--text); }
90 | .comment { font-family:Verdana, Geneva, sans-serif; font-size: 9pt; }
91 | .hnname { margin-right: 5px; }
92 |
93 | .comment a:link, .comment a:visited { text-decoration: underline; }
94 | .noshow { display: none; }
95 | .nosee { visibility: hidden; pointer-events: none; cursor: default }
96 |
97 | .c00, .c00 a:link { color:var(--text-strong); }
98 | .c5a, .c5a a:link, .c5a a:visited { color:var(--c5a); }
99 | .c73, .c73 a:link, .c73 a:visited { color:var(--c73); }
100 | .c82, .c82 a:link, .c82 a:visited { color:var(--text); }
101 | .c88, .c88 a:link, .c88 a:visited { color:var(--c88); }
102 | .c9c, .c9c a:link, .c9c a:visited { color:var(--c9c); }
103 | .cae, .cae a:link, .cae a:visited { color:var(--cae); }
104 | .cbe, .cbe a:link, .cbe a:visited { color:var(--cbe); }
105 | .cce, .cce a:link, .cce a:visited { color:var(--cce); }
106 | .cdd, .cdd a:link, .cdd a:visited { color:var(--cdd); }
107 |
108 | .pagetop a:visited { color:var(--text-strong);}
109 | .topsel a:link, .topsel a:visited { color:#ffffff; }
110 |
111 | .subtext a:link, .subtext a:visited { color:var(--text); }
112 | .subtext a:hover { text-decoration:underline; }
113 |
114 | .comhead a:link, .subtext a:visited { color:var(--text); }
115 | .comhead a:hover { text-decoration:underline; }
116 |
117 | .hnmore a:link, a:visited { color:var(--text); }
118 | .hnmore { text-decoration:underline; }
119 |
120 | .default p { margin-top: 8px; margin-bottom: 0px; }
121 |
122 | .pagebreak {page-break-before:always}
123 |
124 | pre { overflow: auto; padding: 2px; }
125 | pre:hover { overflow:auto }
126 |
127 | .votearrow {
128 | width: 10px;
129 | height: 10px;
130 | border: 0px;
131 | margin: 3px 2px 6px;
132 | background: url("https://jgthms.com/hacker-news-dark-mode/images/grayarrow.gif")
133 | no-repeat;
134 | }
135 |
136 | :root[data-theme="dark"] .votearrow {
137 | background-image: url("https://jgthms.com/hacker-news-dark-mode/images/darkbluearrow.png");
138 | }
139 |
140 | .votelinks.nosee div.votearrow.rotate180 {
141 | display: none;
142 | }
143 |
144 | @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
145 | .votearrow { background-size: 10px; background-image: url("https://jgthms.com/hacker-news-dark-mode/images/grayarrow2x.gif"); }
146 | :root[data-theme="dark"] .votearrow { background-image: url("https://jgthms.com/hacker-news-dark-mode/images/darkbluearrow@2x.png");}
147 | }
148 |
149 | .rotate180 {
150 | -webkit-transform: rotate(180deg); /* Chrome and other webkit browsers */
151 | -moz-transform: rotate(180deg); /* FF */
152 | -o-transform: rotate(180deg); /* Opera */
153 | -ms-transform: rotate(180deg); /* IE9 */
154 | transform: rotate(180deg); /* W3C complaint browsers */
155 |
156 | /* IE8 and below */
157 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=-1, M12=0, M21=0, M22=-1, DX=0, DY=0, SizingMethod='auto expand')";
158 | }
159 |
160 | /* mobile device */
161 | @media only screen
162 | and (min-width : 300px)
163 | and (max-width : 750px) {
164 | #hnmain { width: 100%; }
165 | body { padding: 0; margin: 0; width: 100%; -webkit-text-size-adjust: none; }
166 | td { height: inherit !important; }
167 | .title, .comment { font-size: inherit; }
168 | span.pagetop { display: block; margin: 3px 5px; font-size: 12px; }
169 | span.pagetop b { display: block; font-size: 15px; }
170 | table.comment-tree .comment a { display: inline-block; max-width: 200px; overflow: hidden; white-space: nowrap;
171 | text-overflow: ellipsis; vertical-align:top; }
172 | img[src$='/s.gif'][width='40'] { width: 12px; }
173 | img[src$='/s.gif'][width='80'] { width: 24px; }
174 | img[src$='/s.gif'][width='120'] { width: 36px; }
175 | img[src$='/s.gif'][width='160'] { width: 48px; }
176 | img[src$='/s.gif'][width='200'] { width: 60px; }
177 | img[src$='/s.gif'][width='240'] { width: 72px; }
178 | img[src$='/s.gif'][width='280'] { width: 84px; }
179 | img[src$='/s.gif'][width='320'] { width: 96px; }
180 | img[src$='/s.gif'][width='360'] { width: 108px; }
181 | img[src$='/s.gif'][width='400'] { width: 120px; }
182 | img[src$='/s.gif'][width='440'] { width: 132px; }
183 | img[src$='/s.gif'][width='480'] { width: 144px; }
184 | img[src$='/s.gif'][width='520'] { width: 156px; }
185 | img[src$='/s.gif'][width='560'] { width: 168px; }
186 | img[src$='/s.gif'][width='600'] { width: 180px; }
187 | img[src$='/s.gif'][width='640'] { width: 192px; }
188 | img[src$='/s.gif'][width='680'] { width: 204px; }
189 | img[src$='/s.gif'][width='720'] { width: 216px; }
190 | img[src$='/s.gif'][width='760'] { width: 228px; }
191 | img[src$='/s.gif'][width='800'] { width: 240px; }
192 | img[src$='/s.gif'][width='840'] { width: 252px; }
193 | .title { font-size: 11pt; line-height: 14pt; }
194 | .subtext { font-size: 9pt; }
195 | .itemlist { padding-right: 5px;}
196 | .votearrow { transform: scale(1.3,1.3); margin-right: 6px; }
197 | .votearrow.rotate180 {
198 | -webkit-transform: rotate(180deg) scale(1.3,1.3); /* Chrome and other webkit browsers */
199 | -moz-transform: rotate(180deg) scale(1.3,1.3); /* FF */
200 | -o-transform: rotate(180deg) scale(1.3,1.3); /* Opera */
201 | -ms-transform: rotate(180deg) scale(1.3,1.3); /* IE9 */
202 | transform: rotate(180deg) scale(1.3,1.3); /* W3C complaint browsers */
203 | }
204 | .votelinks { min-width: 18px; }
205 | .votelinks a { display: block; margin-bottom: 9px; }
206 | input[type='text'], input[type='number'], textarea { font-size: 16px; width: 90%; }
207 | }
208 |
209 | .comment { max-width: 1215px; overflow: hidden }
210 | pre { max-width: 900px; }
211 |
212 | @media only screen and (min-width : 300px) and (max-width : 389px) {
213 | .comment { max-width: 270px; overflow: hidden }
214 | pre { max-width: 200px; }
215 | }
216 | @media only screen and (min-width : 390px) and (max-width : 509px) {
217 | .comment { max-width: 350px; overflow: hidden }
218 | pre { max-width: 260px; }
219 | }
220 | @media only screen and (min-width : 510px) and (max-width : 599px) {
221 | .comment { max-width: 460px; overflow: hidden }
222 | pre { max-width: 340px; }
223 | }
224 | @media only screen and (min-width : 600px) and (max-width : 689px) {
225 | .comment { max-width: 540px; overflow: hidden }
226 | pre { max-width: 400px; }
227 | }
228 | @media only screen and (min-width : 690px) and (max-width : 809px) {
229 | .comment { max-width: 620px; overflow: hidden }
230 | pre { max-width: 460px; }
231 | }
232 | @media only screen and (min-width : 810px) and (max-width : 899px) {
233 | .comment { max-width: 730px; overflow: hidden }
234 | pre { max-width: 540px; }
235 | }
236 | @media only screen and (min-width : 900px) and (max-width : 1079px) {
237 | .comment { max-width: 810px; overflow: hidden }
238 | pre { max-width: 600px; }
239 | }
240 | @media only screen and (min-width : 1080px) and (max-width : 1169px) {
241 | .comment { max-width: 970px; overflow: hidden }
242 | pre { max-width: 720px; }
243 | }
244 | @media only screen and (min-width : 1170px) and (max-width : 1259px) {
245 | .comment { max-width: 1050px; overflow: hidden }
246 | pre { max-width: 780px; }
247 | }
248 | @media only screen and (min-width : 1260px) and (max-width : 1349px) {
249 | .comment { max-width: 1130px; overflow: hidden }
250 | pre { max-width: 840px; }
251 | }
252 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgthms/hacker-news-dark-mode/74403d5c672cd67b7a9bf2de6bbd774021805441/favicon.ico
--------------------------------------------------------------------------------
/images/darkbluearrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgthms/hacker-news-dark-mode/74403d5c672cd67b7a9bf2de6bbd774021805441/images/darkbluearrow.png
--------------------------------------------------------------------------------
/images/darkbluearrow2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgthms/hacker-news-dark-mode/74403d5c672cd67b7a9bf2de6bbd774021805441/images/darkbluearrow2x.png
--------------------------------------------------------------------------------
/images/grayarrow.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgthms/hacker-news-dark-mode/74403d5c672cd67b7a9bf2de6bbd774021805441/images/grayarrow.gif
--------------------------------------------------------------------------------
/images/grayarrow2x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgthms/hacker-news-dark-mode/74403d5c672cd67b7a9bf2de6bbd774021805441/images/grayarrow2x.gif
--------------------------------------------------------------------------------
/images/s.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgthms/hacker-news-dark-mode/74403d5c672cd67b7a9bf2de6bbd774021805441/images/s.gif
--------------------------------------------------------------------------------
/images/y18.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgthms/hacker-news-dark-mode/74403d5c672cd67b7a9bf2de6bbd774021805441/images/y18.gif
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Hacker News
11 |
12 |
13 |
14 |
15 |
16 |
17 |
35 |
36 |
37 |
38 |
39 |
586 |
587 |
588 |
589 |
590 |
591 |
596 |
597 |
598 | Guidelines
599 | | FAQ
600 | | Support
601 | | API
602 | | Security
603 | | Lists
604 | | Bookmarklet
605 | | Legal
606 | | Apply to YC
607 | | Contact
608 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
--------------------------------------------------------------------------------
/item.html:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 |
5 |
6 |
7 |
8 |
9 | Ask HN: Dark mode for HN please? | Hacker News
10 |
11 |
12 |
13 |
14 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/js/dark.js:
--------------------------------------------------------------------------------
1 | document.addEventListener("DOMContentLoaded", function() {
2 |
3 | const themeToggle = document.getElementById("theme");
4 | themeToggle.addEventListener("change", switchTheme, false);
5 |
6 | function switchTheme(e) {
7 | if (e.target.checked) {
8 | return document.documentElement.setAttribute("data-theme", "dark");
9 | }
10 | document.documentElement.setAttribute("data-theme", "light");
11 | }
12 |
13 | });
14 |
--------------------------------------------------------------------------------
/js/hn.js:
--------------------------------------------------------------------------------
1 | function $(id) { return document.getElementById(id); }
2 | function byClass (el, cl) { return el ? el.getElementsByClassName(cl) : [] }
3 | function byTag (el, tg) { return el ? el.getElementsByTagName(tg) : [] }
4 | function allof (cl) { return byClass(document, cl) }
5 | function hasClass (el, cl) { var a = el.className.split(' '); return afind(cl, a) }
6 | function addClass (el, cl) { if (el) { var a = el.className.split(' '); if (!afind(cl, a)) { a.unshift(cl); el.className = a.join(' ')}} }
7 | function remClass (el, cl) { if (el) { var a = el.className.split(' '); arem(a, cl); el.className = a.join(' ') } }
8 | function html (el) { return el ? el.innerHTML : null; }
9 | function attr (el, name) { return el.getAttribute(name) }
10 | function tonum (x) { var n = parseFloat(x); return isNaN(n) ? null : n }
11 | function remEl (el) { el.parentNode.removeChild(el) }
12 | function posf (f, a) { for (var i=0; i < a.length; i++) { if (f(a[i])) return i; } return -1; }
13 | function apos (x, a) { return (typeof x == 'function') ? posf(x,a) : Array.prototype.indexOf.call(a,x) }
14 | function afind (x, a) { var i = apos(x, a); return (i >= 0) ? a[i] : null; }
15 | function acut (a, m, n) { return Array.prototype.slice.call(a, m, n) }
16 | function aeach (fn, a) { return Array.prototype.forEach.call(a, fn) }
17 | function arem (a, x) { var i = apos(x, a); if (i >= 0) { a.splice(i, 1); } return a; }
18 | function alast (a) { return a[a.length - 1] }
19 | function vis(el, on) { if (el) { (on ? remClass : addClass)(el, 'nosee') } }
20 | function noshow (el) { addClass(el, 'noshow') }
21 | function elShow (el) { remClass(el, 'noshow') }
22 | function ind (el) { return (byTag(el, 'img')[0] || {}).width }
23 |
24 | function vote(ev, el, how) {
25 | var id = el.id.split(/_/)[1];
26 | var up = $('up_' + id);
27 | vis(up, how == 'un');
28 | vis($('down_' + id), how == 'un');
29 | var unv = '';
30 | if (how != 'un') {
31 | unv = " | " + (how == 'up' ? 'unvote' : 'undown') + " "
35 | }
36 | $('unv_' + id).innerHTML = unv;
37 | new Image().src = el.href;
38 | ev.stopPropagation();
39 | return false;
40 | }
41 |
42 | function comments () { return allof('comtr') }
43 | function collapsed () { return allof('coll') }
44 |
45 | function kidsOf (id) {
46 | var ks = [];
47 | var trs = comments();
48 | var i = apos($(id), trs);
49 | if (i >= 0) {
50 | ks = acut(trs, i + 1);
51 | var n = ind($(id));
52 | var j = apos(function(tr) {return ind(tr) <= n}, ks);
53 | if (j >= 0) { ks = acut(ks, 0, j) }
54 | }
55 | return ks;
56 | }
57 |
58 | function toggle (ev, id) {
59 | var on = !afind($(id), collapsed());
60 | (on ? addClass : remClass)($(id), 'coll');
61 | recoll();
62 | if ($('logout')) {
63 | new Image().src = 'collapse?id=' + id + (on ? '' : '&un=true');
64 | }
65 | ev.stopPropagation();
66 | return false;
67 | }
68 |
69 | function expand (tr) {
70 | elShow(tr);
71 | elShow(byClass(tr, 'comment')[0]);
72 | vis(byClass(tr, 'votelinks')[0], true);
73 | byClass(tr, 'togg')[0].innerHTML = '[–]';
74 | }
75 |
76 | function squish (tr) {
77 | if (hasClass(tr, 'noshow')) return;
78 | aeach(noshow, kidsOf(tr.id));
79 | var el = byClass(tr, 'togg')[0];
80 | el.innerHTML = '[' + el.getAttribute('n') + ' more]';
81 | noshow(byClass(tr, 'comment')[0]);
82 | vis(byClass(tr, 'votelinks')[0], false);
83 | }
84 |
85 | function recoll() {
86 | aeach(expand, comments());
87 | aeach(squish, collapsed());
88 | }
89 |
90 | function onready () {
91 | recoll();
92 | }
93 |
94 | document.addEventListener("DOMContentLoaded", onready);
95 |
96 | function ajax (fn, url) {
97 | var req = new XMLHttpRequest();
98 | req.open('GET', url, true);
99 | req.onreadystatechange = function () {
100 | if (req.readyState === 4 && req.status === 200) {
101 | fn(req.responseText)
102 | }
103 | }
104 | return req.send();
105 | }
106 |
107 | function onop () { return attr(byTag(document,'html')[0],'op') }
108 |
109 | function ranknum (el) {
110 | var s = html(el) || "";
111 | var a = s.match(/[0-9]+/);
112 | if (a) {
113 | return tonum(a[0]);
114 | }
115 | }
116 |
117 | var n1 = ranknum(allof('rank')[0]) || 1;
118 |
119 | function newstory (json) {
120 | if (json) {
121 | var pair = JSON.parse(json);
122 | var sp = alast(allof('spacer'));
123 | sp.insertAdjacentHTML('afterend', pair[0] + sp.outerHTML);
124 | fixranks();
125 | if (onop() == 'newest') {
126 | var n = ranknum(alast(allof('rank')));
127 | allof('morelink')[0].href = 'newest?next=' + pair[1] + '&n=' + (n + 1);
128 | }
129 | }
130 | }
131 |
132 | function fixranks () {
133 | var rks = allof('rank');
134 | aeach(function (rk) { rk.innerHTML = (apos(rk,rks) + n1) + '.' }, rks);
135 | }
136 |
137 | function moreurl() { return allof('morelink')[0].href }
138 | function morenext () { return tonum(moreurl().split('next=')[1]) }
139 |
140 | function hidestory (ev, el, id) {
141 | for (var i=0; i < 3; i++) { remEl($(id).nextSibling) }
142 | remEl($(id));
143 | fixranks();
144 | var next = (onop() == 'newest' && morenext()) ? ('&next=' + morenext()) : ''
145 | var url = el.href.replace('hide', 'snip-story').replace('goto', 'onop')
146 | ajax(newstory, url + next);
147 | ev.stopPropagation();
148 | return false;
149 | }
150 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgthms/hacker-news-dark-mode/74403d5c672cd67b7a9bf2de6bbd774021805441/screenshot.png
--------------------------------------------------------------------------------
We can add CSS to https://news.ycombinator.com/news.css for prefers-color-scheme: dark, but that leaves open the question of specifically what CSS to put in there. Anyone who wants to make a suggestion is welcome to. Post it in this thread so others can comment, or email it to hn@ycombinator.com. I've roped Zain, YC's designer, into helping with this, and we'll come up with something.
p.s. If you're inclined to post "this is 2020, how come HN doesn't $thing", remember our motto: move slowly and preserve things: https://hn.algolia.com/?dateRange=all&page=0&prefix=true&que.... When I say slowly I mean slowly. This is also called alligator energy. https://news.ycombinator.com/item?id=16442716 80 |
81 | reply 82 | 83 |