├── .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 | Hacker News Dark Mode screenshot 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 | 587 | 588 | 589 | 613 | 614 |
39 | 40 | 41 | 42 | 49 | 50 | 51 | 52 | 53 | 56 | 57 | 58 | 59 | 60 | 67 | 68 | 69 | 70 | 71 | 74 | 75 | 76 | 77 | 78 | 85 | 86 | 87 | 88 | 89 | 92 | 93 | 94 | 95 | 96 | 103 | 104 | 105 | 106 | 107 | 110 | 111 | 112 | 113 | 114 | 121 | 122 | 123 | 124 | 125 | 128 | 129 | 130 | 131 | 132 | 139 | 140 | 141 | 142 | 143 | 146 | 147 | 148 | 149 | 150 | 157 | 158 | 159 | 160 | 161 | 164 | 165 | 166 | 167 | 168 | 175 | 176 | 177 | 178 | 179 | 182 | 183 | 184 | 185 | 186 | 193 | 194 | 195 | 196 | 197 | 200 | 201 | 202 | 203 | 204 | 211 | 212 | 213 | 214 | 215 | 218 | 219 | 220 | 221 | 222 | 229 | 230 | 231 | 232 | 233 | 236 | 237 | 238 | 239 | 240 | 247 | 248 | 249 | 250 | 251 | 254 | 255 | 256 | 257 | 258 | 265 | 266 | 267 | 268 | 269 | 272 | 273 | 274 | 275 | 276 | 283 | 284 | 285 | 286 | 287 | 290 | 291 | 292 | 293 | 294 | 301 | 302 | 303 | 304 | 305 | 308 | 309 | 310 | 311 | 312 | 319 | 320 | 321 | 322 | 323 | 326 | 327 | 328 | 329 | 330 | 337 | 338 | 339 | 340 | 341 | 344 | 345 | 346 | 347 | 348 | 355 | 356 | 357 | 358 | 359 | 362 | 363 | 364 | 365 | 366 | 373 | 374 | 375 | 376 | 377 | 380 | 381 | 382 | 383 | 384 | 391 | 392 | 393 | 394 | 395 | 398 | 399 | 400 | 401 | 402 | 409 | 410 | 411 | 412 | 413 | 416 | 417 | 418 | 419 | 420 | 427 | 428 | 429 | 430 | 431 | 434 | 435 | 436 | 437 | 438 | 445 | 446 | 447 | 448 | 449 | 452 | 453 | 454 | 455 | 456 | 463 | 464 | 465 | 466 | 467 | 470 | 471 | 472 | 473 | 474 | 481 | 482 | 483 | 484 | 485 | 488 | 489 | 490 | 491 | 492 | 499 | 500 | 501 | 502 | 503 | 506 | 507 | 508 | 509 | 510 | 517 | 518 | 519 | 520 | 521 | 524 | 525 | 526 | 527 | 528 | 535 | 536 | 537 | 538 | 539 | 542 | 543 | 544 | 545 | 546 | 553 | 554 | 555 | 556 | 557 | 560 | 561 | 562 | 563 | 564 | 571 | 572 | 573 | 574 | 575 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 |
1.Unikernels: The Next Stage of Linux’s Dominance (2019) (acm.org)
54 | 107 points by todsacerdoti 3 hours ago | hide | 54 comments 55 |
2.Bizarre new species discovered on Twitter (phys.org)
72 | 32 points by dnetesn 56 minutes ago | hide | 8 comments 73 |
3.Swift 5.3 Will Be Supported on Windows and Additional Linux Distributions (infoq.com)
90 | 328 points by nan0 11 hours ago | hide | 193 comments 91 |
4.History of Lisp (1979) [pdf] (stanford.edu)
108 | 21 points by alokrai 1 hour ago | hide | 3 comments 109 |
5.uBlock Origin on Firefox Preview (github.com)
126 | 149 points by surround 7 hours ago | hide | 35 comments 127 |
6.Amazon is handing out 'Thank you' t-shirts to warehouse workers as it cuts pay (businessinsider.com)
144 | 102 points by doener 2 hours ago | hide | 71 comments 145 |
7.Shibuya Pixel Art 2020 (lexaloffle.com)
162 | 90 points by polm23 7 hours ago | hide | 20 comments 163 |
8.Justice Department, states likely to bring antitrust lawsuits against Google (wsj.com)
180 | 422 points by xoxoy 14 hours ago | hide | 408 comments 181 |
9.Have the Record Number of Investors in the Stock Market Lost Their Minds? (newyorker.com)
198 | 40 points by seek3r00 5 hours ago | hide | 34 comments 199 |
10.Factors associated with Covid-19 deaths in records of 17M adult NHS patients (twitter.com)
216 | 162 points by gandalfian 11 hours ago | hide | 132 comments 217 |
11.Modern C++ gamedev: thoughts and misconceptions (vittorioromeo.info)
234 | 7 points by ingve 1 hour ago | hide | 1 comment 235 |
12.Google Drive Desktop – A cross-platform Google Drive desktop app made w Electron (github.com)
252 | 166 points by mrchaniku 12 hours ago | hide | 73 comments 253 |
13.DIY Particle Detector (cern.ch)
270 | 166 points by homarp 15 hours ago | hide | 33 comments 271 |
14.Under AI’s Watchful Eye, China Wants to Raise Smarter Students (2019) [video] (wsj.com)
288 | 32 points by seventyhorses 7 hours ago | hide | 33 comments 289 |
15.Poi: Pragmatic point-free theorem prover assistant in Rust (github.com)
306 | 34 points by adamnemecek 6 hours ago | hide | 4 comments 307 |
16.Facebook to Buy Giphy for $400M (axios.com)
324 | 882 points by coloneltcb 20 hours ago | hide | 482 comments 325 |
17.Ask HN: Dark mode for HN please?
342 | 547 points by krm01 13 hours ago | hide | 329 comments 343 |
18.SubEthaEdit 5 – native open-source text editor with live collaboration feature (subethaedit.net)
360 | 57 points by guessmyname 9 hours ago | hide | 14 comments 361 |
19.The Great CoffeeScript to Typescript Migration of 2017 (dropbox.tech)
378 | 267 points by dgoldstein0 15 hours ago | hide | 173 comments 379 |
20.The boring technology behind a one-person Internet company (listennotes.com)
396 | 195 points by dilly_li 15 hours ago | hide | 22 comments 397 |
21.OpenBSD on the Microsoft Surface Go 2 (jcs.org)
414 | 4 points by rodrigo975 2 hours ago | hide | discuss 415 |
22.Warren Buffett’s Berkshire dumps most of Goldman Sachs stake (ft.com)
432 | 58 points by yarapavan 4 hours ago | hide | 11 comments 433 |
23.Sony Builds AI into a CMOS Image Sensor (ieee.org)
450 | 71 points by anandaverma18 13 hours ago | hide | 20 comments 451 |
24.Python – Writing large ZIP archives without memory inflation (github.com)
468 | 90 points by sandes 12 hours ago | hide | 33 comments 469 |
25.So much of academia is about connections and reputation laundering (columbia.edu)
486 | 345 points by luu 14 hours ago | hide | 191 comments 487 |
26.Google Erases Thousands of Links, Tricked by Phony Complaints (wsj.com)
504 | 291 points by known 16 hours ago | hide | 109 comments 505 |
27.State of Loom (java.net)
522 | 6 points by mxschumacher 2 hours ago | hide | discuss 523 |
28.A happy ending for Seattle’s Bop Street Records: a nonprofit buys the collection (seattletimes.com)
540 | 30 points by wallflower 10 hours ago | hide | 9 comments 541 |
29.Gilead should ditch remdesivir and focus on its simpler and safer ancestor (statnews.com)
558 | 170 points by phonon 14 hours ago | hide | 81 comments 559 |
30.Mozilla goes incubator with ‘Fix The Internet’ startup early-stage investments (techcrunch.com)
576 | 340 points by cpeterso 18 hours ago | hide | 143 comments 577 |
586 |
590 | 591 | 592 | 593 | 594 | 595 |
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 |
Search: 609 | 610 |
611 |
612 |
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 | 15 | 16 | 34 | 35 | 36 | 37 | 85 | 86 | 87 | 111 | 112 |
38 | 39 | 40 | 41 | 48 | 49 | 50 | 51 | 52 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 69 | 70 |
Ask HN: Dark mode for HN please?
53 | 575 points by krm01 14 hours ago | flag | hide | past | web | favorite | 346 comments 54 |
Of all places, HN should be dark mode by default, without any of us using a plugin or specific browser. I don't want my eyes to burn when I'm browsing HN at night on my phone. Anyone?
65 |
66 |

67 |
68 |
71 |
72 |
73 | 74 |
75 |
77 | dang 12 hours ago [–] 78 |

79 | Ok, you guys, this isn't the first time we've heard this request (https://hn.algolia.com/?dateRange=all&page=0&prefix=true&que...). I'm willing to do it (edit: not to change the default! just to add the option). It's just that any CSS issue that goes more than a quarter-inch deep is equally outside my expertise and my interest, so help would be welcome.

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 |

84 |
88 | 89 | 90 | 91 | 92 | 93 |
94 |
95 |
96 | Guidelines 97 | | FAQ 98 | | Support 99 | | API 100 | | Security 101 | | Lists 102 | | Bookmarklet 103 | | Legal 104 | | Apply to YC 105 | | Contact

106 |
Search: 107 | 108 |
109 |
110 |
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 --------------------------------------------------------------------------------