Logo

index : link-collection

---

  • summary
  • about
  • tree
  • log
  • branches
<< path: root/public/link-collection.git/html/www/index.html blob: 7f1a7ad1c44d1dbe07725ee3db00e71b8bf21cab [raw] [clear marker]

        
0<!DOCTYPE html>
1<html>
2<head>
3 <meta charset="UTF-8">
4 <meta name="viewport" content="width=device-width, initial-scale=1.0">
5 <link rel="icon" type="image/x-icon" href="img/guksu_logo.png">
6 <title>guksu - link collection</title>
7 <style>
8 :root {
9 --t-title-anim-duration: 5s;
10 --no-ads-scroll-speed: 180s;
11
12 --link-color: green;
13 --link-hover: red;
14 --link-active: yellow;
15 --bg-color: #030304;
16
17 /* 80s colors */
18 --clr-red1: hsl(349, 71%, 70%);
19 --clr-red2: hsl(345, 73%, 32%);
20 --clr-red3: hsl(345, 90%, 60%);
21 --clr-blue1: hsl(199, 58%, 53%);
22 --clr-blue2: hsl(191, 76%, 71%);
23 --clr-green-dark: hsl(160, 99%, 10%);
24 --clr-green0: hsl(160, 99%, 40%);
25 --clr-green1: hsl(160, 99%, 53%);
26 --clr-green2: hsl(160, 99%, 70%);
27 --clr-green3: hsl(160, 99%, 82%);
28 --clr-orange1: hsl(28, 68%, 42%);
29 --clr-yellow1: hsl(35, 79%, 65%);
30 --clr-purple1: hsl(317, 73%, 47%);
31 --clr-purple2: hsl(320, 100%, 52%);
32
33 /* pixels */
34 --title-offset: 2px;
35 }
36 body {
37 margin: 0;
38 background-color: var(--bg-color);
39 color: #e9e9fc;
40 color-scheme: dark;
41 font-family: Verdana, Geneva, Tahoma, sans-serif;
42 }
43 h2 {
44 color: var(--clr-blue2);
45 text-shadow: 0 0 12px var(--clr-blue1);
46 margin-top: 1.2em;
47 padding-bottom: 0.5em;
48 border-bottom: solid 1px var(--clr-blue2);
49 }
50 .main-wrapper {
51 display: flex;
52 flex-flow: column nowrap;
53 height: 100vh;
54 }
55 .screen-stripes {
56 z-index: 19;
57 position: absolute;
58 width: 100vw;
59 height: 100vh;
60 pointer-events: none;
61
62 &::before {
63 content: "";
64 background: repeating-linear-gradient(
65 var(--clr-green2),
66 var(--clr-green2) 1px,
67 transparent 1px,
68 transparent 3px
69 );
70 opacity: 0.04;
71 top: 0;
72 left: 0;
73 position: absolute;
74 width: 100%;
75 height: 100%;
76 }
77 }
78 .screen-grain {
79 z-index: 20;
80 position: absolute;
81 width: 100vw;
82 height: 100vh;
83 pointer-events: none;
84
85 &::before {
86 content: "";
87 background-color: hsl(191, 76%, 34%);
88 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 600'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");
89 background-repeat: repeat;
90 background-size: 182px;
91 opacity: 0.07;
92 top: 0;
93 left: 0;
94 position: absolute;
95 width: 100%;
96 height: 100%;
97 }
98 }
99 .make-it-bigger {
100 display: flex;
101 flex-flow: column wrap;
102 z-index: 15;
103 position: absolute;
104 top: 0;
105 right: 0;
106 width: 2px;
107 height: 2px;
108 background-color: #000;
109 cursor: zoom-in;
110 user-select: none;
111 animation: mib-shine 15s linear infinite;
112 align-items: center;
113 justify-content: center;
114 font-size: 1.4em;
115 color: var(--clr-green2);
116 & p {
117 padding: 1em;
118 margin: 0.2em;
119 }
120 }
121 @keyframes mib-shine {
122 0% { background-color: transparent; }
123 98% { background-color: transparent; }
124 99% { background-color: var(--clr-green3); }
125 100% { background-color: transparent; }
126 }
127 .blinky::after {
128 content: "▌";
129 opacity: 1;
130 color: var(--clr-green2);
131 padding-left: 0.3em;
132 animation: blinky-blink 1.25s linear infinite;
133 }
134 @keyframes blinky-blink {
135 0% { opacity: 1; }
136 49% { opacity: 1; }
137 49.9% { opacity: 0; }
138 99.9% { opacity: 0; }
139 100% { opacity: 1; }
140 }
141 .nav {
142 display: flex;
143 flex-flow: row wrap;
144 font-size: 1.0rem;
145 padding: 0.5em;
146 }
147 .nav ul, .menu ul {
148 margin: 0;
149 list-style: none;
150 padding-left: 0;
151 }
152 .nav li, .menu li {
153 float: left;
154 list-style: none;
155 }
156 .nav::before {
157 color: var(--clr-green1);
158 content: "Categories:";
159 animation: nav-cat-shadow 9s linear infinite;
160 }
161 @keyframes nav-cat-shadow {
162 0% { text-shadow: 1px 1px 30px var(--clr-blue2); }
163 30% { text-shadow: 1px 1px 30px var(--clr-blue2); }
164 50% { text-shadow: 1px 1px 80px var(--clr-blue2); }
165 60% { text-shadow: 1px 1px 80px var(--clr-blue2); }
166 100% { text-shadow: 1px 1px 30px var(--clr-blue2); }
167 }
168 .nav a::before {
169 content: "/";
170 color: var(--clr-purple2);
171 padding-right: 0.2em;
172 padding-left: 0.2em;
173 }
174 .nav a, .nav a:visited {
175 color: var(--clr-blue2);
176 text-decoration: none;
177 }
178 .nav a:hover {
179 color: var(--clr-green1);
180 text-shadow: 1px 1px 8px var(--clr-green1);
181 }
182 .nav a:active {
183 color: var(--clr-red1);
184 text-shadow: none;
185 }
186 .menu {
187 z-index: 3;
188 position: relative;
189 margin-inline: auto;
190 margin-top: 0.5em;
191 margin-bottom: 3em;
192
193 &::after {
194 z-index: -1;
195 position: absolute;
196 content: "";
197 top: 0;
198 left: 0;
199 height: 100%;
200 width: 100%;
201 background-color: rgba(86, 247, 201, 0.2);
202 animation: menu-blur 10s linear infinite;
203 }
204 }
205 @keyframes menu-blur {
206 0% { filter: blur(25px); }
207 30% { filter: blur(25px); }
208 50% { filter: blur(50px); }
209 60% { filter: blur(38px); }
210 100% { filter: blur(25px); }
211 }
212 .menu a, .menu a:visited {
213 --shadow-y: 1;
214 color: var(--clr-blue2);
215 text-decoration: none;
216 transition: text-shadow 0.2s ease-out;
217 text-shadow:
218 0px calc(8px * var(--shadow-y)) 1px hsl(160, 99%, 20%),
219 0px calc(16px * var(--shadow-y)) 1px hsl(320, 100%, 15%),
220 0px calc(24px * var(--shadow-y)) 1px hsl(345, 73%, 10%),
221 0px calc(32px * var(--shadow-y)) 1px hsl(360, 65%, 5%);
222 }
223 .menu a:hover {
224 --shadow-y: 0;
225 color: var(--clr-green1);
226 }
227 .menu a:active {
228 --shadow-y: 1;
229 color: var(--clr-purple2);
230 }
231 .menu a::before {
232 content: "[";
233 padding-right: 0.15em;
234 padding-left: 0.2em;
235 }
236 .menu a::after {
237 content: "]";
238 padding-right: 0.2em;
239 padding-left: 0.15em;
240 }
241 .no-ads {
242 z-index: 1;
243 display: flex;
244 align-items: center;
245 margin: 0;
246 width: 100%;
247 overflow: clip;
248 }
249 .no-ads-row {
250 display: flex;
251 width: max-content;
252 animation: scroll var(--no-ads-scroll-speed) linear infinite;
253 &:hover {
254 cursor: wait;
255 animation-play-state: paused;
256 }
257 }
258 .anim-stop {
259 animation-play-state: paused;
260 }
261 .no-ads-row > div {
262 width: 1280px;
263 height: 50px;
264 flex-shrink: 0;
265
266 & img {
267 width: 100%;
268 height: 100%;
269 }
270 }
271 @keyframes scroll {
272 to { transform: translateX(-50%); }
273 }
274 @media (max-width: 540px) {
275 .btn-anim-toggle {
276 visibility: hidden;
277 }
278 }
279 .btn-anim-toggle {
280 text-align: right;
281 padding: 1em 0.5em 0 0;
282 }
283 .btn-anim-toggle button {
284 color: var(--clr-green1);
285 padding: 0.3em;
286 border: 0;
287 background-color: transparent;
288 text-shadow: 0px 1px 6px var(--clr-green1);
289
290 &:visited {
291 color: var(--clr-green1);
292 }
293
294 &:hover {
295 color: var(--clr-purple1);
296 text-shadow: 0px 1px 6px var(--clr-purple2);
297 cursor: pointer;
298 }
299
300 &:active {
301 color: var(--clr-purple2);
302 }
303 }
304 .tunnel-fx {
305 z-index: 5;
306 display: flex;
307 flex-flow: row nowrap;
308 position: absolute;
309 width: inherit;
310 pointer-events: none;
311 }
312 .tunnel-fx-sides {
313 --tunnel-light: 0.35;
314 flex: 1;
315 border-radius: 15px;
316 animation: tunnel-opacity 26s linear infinite;
317 }
318 @keyframes tunnel-opacity {
319 0% { --tunnel-light: 0.35 }
320 38.9% { --tunnel-light: 0.35 }
321 39% { --tunnel-light: 0.1 }
322 39.2% { --tunnel-light: 0.5 }
323 39.4% { --tunnel-light: 0.1 }
324 39.6% { --tunnel-light: 0.5 }
325 39.7% { --tunnel-light: 0.1 }
326 39.8% { --tunnel-light: 0.5 }
327 40% { --tunnel-light: 0.35 }
328 50% { --tunnel-light: 0.35 }
329 70% { --tunnel-light: 0.35 }
330 70.1% { --tunnel-light: 0.1 }
331 70.2% { --tunnel-light: 0.5 }
332 70.4% { --tunnel-light: 0.1 }
333 70.6% { --tunnel-light: 0.5 }
334 70.8% { --tunnel-light: 0.1 }
335 71% { --tunnel-light: 0.35 }
336 100% { --tunnel-light: 0.35 }
337 }
338 .tunel-sides-r {
339 box-shadow: inset 24px 0 20px -20px rgba(14, 254, 174, var(--tunnel-light));
340 }
341 .tunel-sides-l {
342 box-shadow: inset -24px 0 20px -20px rgba(14, 254, 174, var(--tunnel-light));
343 }
344 .tunnel-fx-mid {
345 max-width: 1000px;
346 font-size: 2em;
347 padding: 0.8em;
348 margin: 0 auto;
349 text-shadow:
350 var(--title-offset) var(--title-offset) 1px hsl(175, 76%, 20%),
351 calc(var(--title-offset) * -1) calc(var(--title-offset) * -1) 1px hsl(320, 76%, 26%);
352 animation: rainbow 10s ease infinite;
353 background-color: var(--bg-color);
354 pointer-events: auto;
355
356 & span {
357 display: inline-block;
358 }
359 }
360 .t-title-1 {
361 animation: upDown1 var(--t-title-anim-duration) linear infinite;
362 }
363 .t-title-2 {
364 animation: upDown2 var(--t-title-anim-duration) linear infinite;
365 }
366 .t-title-3 {
367 animation: upDown3 var(--t-title-anim-duration) linear infinite;
368 }
369 .t-title-4 {
370 animation: upDown4 var(--t-title-anim-duration) linear infinite, flicker 30s linear infinite;
371 }
372 .t-title-5 {
373 animation: upDown5 var(--t-title-anim-duration) linear infinite;
374 }
375 .t-title-6 {
376 animation: upDown6 var(--t-title-anim-duration) linear infinite;
377 }
378 @keyframes upDown1 {
379 0% { transform: translateY(3px) rotate(-3deg); }
380 60% { transform: translateY(-3px) rotate(-3deg); }
381 80% { transform: translateY(1px) rotate(-3deg); }
382 100% { transform: translateY(3px) rotate(-3deg); }
383 }
384 @keyframes upDown2 {
385 0% { transform: translateY(-2px); }
386 60% { transform: translateY(0px); }
387 65% { transform: translateY(1px); }
388 80% { transform: translateY(0px); }
389 100% { transform: translateY(-2px); }
390 }
391 @keyframes upDown3 {
392 0% { transform: translateY(5px); }
393 40% { transform: translateY(6px); }
394 80% { transform: translateY(3px); }
395 100% { transform: translateY(5px); }
396 }
397 @keyframes upDown4 {
398 0% { transform: translateY(0px) rotate(-3deg); }
399 40% { transform: translateY(-2px) rotate(-3deg); }
400 80% { transform: translateY(-3px) rotate(-3deg); }
401 100% { transform: translateY(0px) rotate(-3deg); }
402 }
403 @keyframes upDown5 {
404 0% { transform: translateY(-4px); }
405 30% { transform: translateY(-10px); }
406 55% { transform: translateY(-8px); }
407 80% { transform: translateY(-2px); }
408 100% { transform: translateY(-4px); }
409 }
410 @keyframes upDown6 {
411 0% { transform: translateY(-2px) rotate(3deg); }
412 30% { transform: translateY(-2px) rotate(10deg); }
413 55% { transform: translateY(-2px) rotate(6deg); }
414 80% { transform: translateY(0px) rotate(2deg); }
415 100% { transform: translateY(-2px) rotate(3deg); }
416 }
417 @keyframes flicker {
418 0% { opacity: 1; }
419 7.8% { opacity: 1; }
420 7.9% { opacity: 0; }
421 8% { opacity: 1; }
422 8.3% { opacity: 0; }
423 8.9% { opacity: 1; }
424 9% { opacity: 0; }
425 11% { opacity: 1; }
426 20% { opacity: 1; }
427 30% { opacity: 1; }
428 40% { opacity: 1; }
429 50% { opacity: 1; }
430 59% { opacity: 1; }
431 59.1% { opacity: 0; }
432 59.2% { opacity: 0.5; }
433 59.3% { opacity: 0; }
434 59.5% { opacity: 0.5; }
435 59.9% { opacity: 0; }
436 60% { opacity: 0; }
437 69.7% { opacity: 0; }
438 69.8% { opacity: 0.5; }
439 69.9% { opacity: 0; }
440 71% { opacity: 1; }
441 80% { opacity: 1; }
442 90% { opacity: 1; }
443 100% { opacity: 1; }
444 }
445 .header {
446 border-top: dotted 1px var(--clr-green1);
447 border-bottom: dashed 1px var(--clr-green1);
448 padding-bottom: 0.12em;
449 margin-bottom: 0.3em;
450 box-shadow: 0px 5px 25px hsl(160, 99%, 22%);
451 }
452 .header-2 {
453 border-bottom: dotted 1px var(--clr-green1);
454 border-top: dashed 1px var(--clr-green1);
455 padding-bottom: 0.1em;
456 margin-bottom: 0.6em;
457 }
458 .header-spacer {
459 margin-top: 0.6em;
460 margin-bottom: 0;
461 }
462 @keyframes rainbow {
463 0% { color: hsl(120, 80%, 53%); }
464 25% { color: hsl(140, 89%, 53%); }
465 50% { color: hsl(160, 99%, 53%); }
466 75% { color: hsl(140, 89%, 53%); }
467 100% { color: hsl(120, 80%, 53%); }
468 }
469 .body {
470 display: flex;
471 flex-flow: column nowrap;
472 flex 1;
473 height: 100%;
474 min-height: 0;
475 width: 100%;
476 max-width: 1000px;
477 margin: 0 auto;
478 padding-top: 1em;
479 }
480 .body footer {
481 overflow-x: hidden;
482 position: relative;
483 text-align: center;
484 margin-top: 1em;
485 border-top: dashed 1px var(--clr-green1);
486 color: var(--clr-green2);
487 text-shadow: 0 0 6px var(--clr-green2);
488
489 & p::before {
490 position: absolute;
491 content: "•";
492 left: 1em;
493 }
494 & p::after {
495 position: absolute;
496 content: "•";
497 right: 1em;
498 }
499 }
500 .content {
501 flex: 1;
502 padding: 2em 1em 1em 1em;
503 min-height: 0;
504 scrollbar-color: var(--clr-blue2) var(--clr-green-dark);
505 scrollbar-width: thin;
506 scrollbar-gutter: stable;
507 overflow-y: auto;
508 overflow-x: hidden;
509 }
510 @media (max-width: 300px) {
511 .big-title {
512 line-break: anywhere;
513 }
514 }
515 .big-title {
516 color: var(--clr-blue2);
517 font-weight: bold;
518 font-style: italic;
519 text-transform: uppercase;
520 animation: big-title-shadow 9s linear infinite;
521 position: relative;
522 text-align: center;
523 font-size: 2em;
524 padding: 0.5em;
525 margin-bottom: 0.5em;
526
527 &::before {
528 content: "";
529 position: absolute;
530 inset: 0;
531 background: var(--clr-purple2);
532
533 /* Corners by https://css-generators.com/custom-corners */
534 clip-path: polygon(0 0,calc(100% - 15px) 0,100% 15px,100% 100%,15px 100%,0 calc(100% - 15px),0 0,1px 1px ,1px calc(100% - 15px - 0.41px),calc(15px + 0.41px) calc(100% - 1px),calc(100% - 1px) calc(100% - 1px),calc(100% - 1px) calc(15px + 0.41px),calc(100% - 15px - 0.41px) 1px,1px 1px);
535 }
536 }
537 @keyframes big-title-shadow {
538 0% { text-shadow: 0px 0px 25px var(--clr-blue1); }
539 68% { text-shadow: 0px 0px 50px var(--clr-blue1); }
540 100% { text-shadow: 0px 0px 25px var(--clr-blue1); }
541 }
542 .big-title-l {
543 bottom: -3px;
544 left: 0;
545 transform: rotate(-45deg);
546 position: absolute;
547 content: "";
548 width: 1px;
549 height: 10px;
550 background-color: var(--clr-green2);
551 box-shadow: 0 0 5px var(--clr-green2);
552 animation: bt-corners 1s ease ;
553 }
554 .big-title-r {
555 top: -3px;
556 right: 0;
557 transform: rotate(-45deg);
558 position: absolute;
559 content: "";
560 width: 1px;
561 height: 10px;
562 background-color: var(--clr-green2);
563 box-shadow: 0 0 5px var(--clr-green2);
564 animation: bt-corners 1s ease 1;
565 }
566 @keyframes bt-corners {
567 from { opacity: 0; }
568 to { opacity: 1;}
569 }
570 .simple-text {
571 padding: 1em 1em 2.5em 1em;
572
573 & a, &:visited {
574 text-decoration: underline;
575 color: var(--clr-green1);
576 }
577 & a:hover {
578 color: var(--clr-green2);
579 text-shadow: 0px 0px 7px var(--clr-green1);
580 }
581 & a:active {
582 color: var(--clr-green0);
583 text-shadow: none;
584 }
585 }
586 .st-center p {
587 text-align: center;
588 }
589 .st-green p {
590 color: var(--clr-green2);
591 }
592 .st-font-small p {
593 font-size: 1.1em;
594 }
595 .st-font-mid p {
596 font-size: 1.3em;
597 }
598 .st-font-bigger p {
599 font-size: 1.5em;
600 }
601 .st-font-big p {
602 font-size: 1.7em;
603 }
604 .collection {
605 color: var(--clr-green0);
606 & > div {
607 border: solid 1px var(--clr-blue1);
608 border-radius: 0 12px 0 12px;
609 padding: 1em;
610 margin-bottom: 1.4em;
611 box-shadow:
612 -1px -1px 0px var(--clr-green1),
613 1px 1px 0px var(--clr-purple2);
614 }
615 & div {
616 & div {
617 padding: 0 1.5em 0.5em 1.5em;
618 }
619 & a {
620 color: var(--clr-blue2);
621 text-decoration: none;
622 &:hover { text-shadow: 0 0 9px var(--clr-blue2); }
623 }
624 & a::after {
625 color: var(--clr-purple1);
626 opacity: 0.6;
627 margin-left: 0.5em;
628 cursor: default;
629 text-shadow: none;
630 }
631 }
632 & div > div { padding-top: 0.5em; }
633 & .l-tech a::after { content: " [tech]"; }
634 & .l-programming a::after { content: " [programming]"; }
635 & .l-art a::after { content: " [art]"; }
636 & .l-misc a::after { content: " [misc]"; }
637 }
638 .rnd-btn {
639 padding: 1em 1.5em 1em 1.5em;
640 color: var(--clr-green2);
641 font-size: 1em;
642 border: solid 1px var(--clr-green2);
643 border-radius: 0.7em;
644 border-inline-width: 7px;
645 box-shadow: inset 0 0 0 5px var(--clr-green0);
646 background-color: transparent;
647 text-shadow: 0 0 10px var(--clr-green0);
648
649 &:hover {
650 text-shadow: 0 0 10px var(--clr-green1);
651 box-shadow: inset 0 0 0 5px var(--clr-green-dark);
652 border-color: var(--clr-green3);
653 cursor: pointer;
654 }
655 &:active {
656 text-shadow: 0 0 7px var(--clr-green1);
657 border-color: var(--clr-green1);
658 transition: box-shadow 0.1s ease, transform 0.75s ease 0.2s;
659 box-shadow: inset 0 0 0 7px var(--clr-green-dark);
660 transform: rotateX(360deg);
661 }
662 }
663 .hide {
664 display: none;
665 }
666 </style>
667</head>
668<body>
669 <div class="main-wrapper">
670 <div class="screen-stripes"></div>
671 <div class="screen-grain"></div>
672 <div class="make-it-bigger"></div>
673 <div class="nav">
674 <ul>
675 <li><a href="#cat-all" title="All Links">all</a></li>
676 <li><a href="#cat-tech" title="Tech Links">tech</a></li>
677 <li><a href="#cat-programming" title="Programming Links">prog</a></li>
678 <li><a href="#cat-art" title="Art Links">art</a></li>
679 <li><a href="#cat-misc" title="Miscellaneous Links">misc</a></li>
680 </ul>
681 </div>
682 <div class="menu">
683 <ul>
684 <li><a href="#random" title="Random Links">Random</a></li>
685 <li><a href="#about" title="About">About</a></li>
686 <li><a href="#report" title="Report a link">Report Link</a></li>
687 <li><a href="https://codeberg.org/ptrace/link-collection" target="_blank" title="Source code">Source</a></li>
688 </ul>
689 </div>
690
691 <noscript>
692 <div style="padding-bottom: 1.5em; text-align: center; color: var(--clr-purple1);">
693 <p>Note: Javascript is not enabled. Since the rendering of the links relies on it.</p>
694 <p>But if you want to access the link collection without JS, you can use my API:
695 <a href="/links" target="_blank" title="API for all links">guksu.ptrace.dev/links</a></p>
696 </div>
697 </noscript>
698
699 <div class="no-ads">
700 <div class="tunnel-fx">
701 <div class="tunnel-fx-sides tunel-sides-l"></div>
702 <div class="tunnel-fx-mid">
703 <span class="t-title-1">welcome</span>
704 <span class="t-title-2">to</span>
705 <span class="t-title-3">my</span>
706 <span class="t-title-4">link</span>
707 <span class="t-title-5">colle</span><span class="t-title-6">ction!</span>
708 </div>
709 <div class="tunnel-fx-sides tunel-sides-r"></div>
710 </div>
711 <div class="no-ads-row anim-stop">
712 <!--
713 Supporting till 3440 x 1440
714 Anything beyond that is just madness.
715
716 The illusion of a infinite animation depends on following aspects:
717 - width of the containers below
718 - number of unique gif's
719 - chunk size (aka one group of unique gif's)
720
721 How many chunks you need is a little bit trial and error, which
722 also depends on the maximum window resolution you want to support.
723
724 In this case, there are 8x 160x50px images. Stiched together to one 1280x50px file.
725 There are to upsides: Less line noise and only a single request, instead of eight.
726 -->
727 <div><img src="img/no_ads.jpg"></div>
728 <div><img src="img/no_ads.jpg"></div>
729 <div><img src="img/no_ads.jpg"></div>
730 <div><img src="img/no_ads.jpg"></div>
731 <div><img src="img/no_ads.jpg"></div>
732 <div><img src="img/no_ads.jpg"></div>
733 <div><img src="img/no_ads.jpg"></div>
734 <div><img src="img/no_ads.jpg"></div>
735
736 </div>
737 </div>
738
739 <div class="btn-anim-toggle">
740 <button id="pause-anim-btn" onclick="toggle_banner_animation()"></button>
741 </div>
742
743 <div class="body">
744 <div class="header-spacer"></div>
745 <div class="header"></div>
746 <div class="header-2"></div>
747 <div class="content">
748
749 <!-- menu content -->
750 <div class="random">
751 <div class="big-title">
752 <span id="title-text">random</span>
753 <div class="big-title-l"></div>
754 <div class="big-title-r"></div>
755 </div>
756 <div class="simple-text st-center st-green st-font-mid">
757 <p>Down below are some random entries.</p>
758 <p>If you want a new set of links, just refresh this site
759 or press (F4) for a hotreload</p>
760
761 <p style="margin-top: 1.4em; font-size: 1.7em;">
762 <button class="rnd-btn" onclick="render_rnd_collection(); return false;">
763 I'm too lazy to press F4 or F5</button></p>
764 </div>
765 <div id="random-collection" class="collection"></div>
766 </div>
767
768 <div class="about">
769 <div class="big-title">
770 <span id="title-text">about</span>
771 <div class="big-title-l"></div>
772 <div class="big-title-r"></div>
773 </div>
774 <div class="simple-text st-green st-font-small">
775 <p>Back in the '90s and early 2000s, exploring the internet was a
776 different experience. Instead of algorithms, we relied on
777 recommendations from friends and curated link collections.</p>
778
779 <p>This project aims to revive that old spirit and contribute to
780 the culture that made the early internet so colorful.</p>
781
782 <p>The source code for this website is open and available
783 <a href="https://codeberg.org/ptrace/link-collection" target="_blank"
784 title="Source Code (Codeberg.org)">here</a>.</p>
785
786 <h2>Privacy</h2>
787 <p>This website does not collect any data, nor it serves any cookies.</p>
788
789 <h2>Disclaimer</h2>
790 <p>I bear no responsibility for the accuracy, legality or content of the
791 external site.</p>
792
793 <p>If an external site appears inappropriate, please
794 <a href="#report" title="Report a link">report</a> it to me.</p>
795 </div>
796 </div>
797 <div class="report">
798 <div class="big-title">
799 <span id="title-text">report link</span>
800 <div class="big-title-l"></div>
801 <div class="big-title-r"></div>
802 </div>
803 <div class="simple-text st-green st-font-small">
804 <p>I'm reviewing each link I publish, so nothing malignant slips through.</p>
805
806 <p>But if a site got hacked, or the web admin decides to go nuts,
807 I'll remove the link as soon as humanly possible.</p>
808
809 <p>If you encounter a malignant link, please report it to me via
810 e-mail:</p>
811
812 <p><a href="mailto:blog@ptrace.dev">blog@ptrace.dev</a>
813 (I'm also supporting GPG -> <a href="https://blog.ptrace.dev/key" target="_blank">Public Key</a>),
814
815 or create a <a href="https://codeberg.org/ptrace/link-collection/issues/new?title=Report+Link" target="_blank" title="creates a new ticket at my code repository">ticket</a> via my repository.</p>
816
817 <p style="font-size: 1.4em; margin: 2em 0 0 1em;">Thanks!</p>
818 </div>
819 </div>
820
821 <!-- category content -->
822 <div class="cat-all">
823 <div class="big-title">
824 <span id="title-text">all links</span>
825 <div class="big-title-l"></div>
826 <div class="big-title-r"></div>
827 </div>
828 <div id="all-links" class="collection cat-content"></div>
829 </div>
830 <div class="cat-tech">
831 <div class="big-title">
832 <span id="title-text">tech</span>
833 <div class="big-title-l"></div>
834 <div class="big-title-r"></div>
835 </div>
836 <div class="collection cat-content"></div>
837 </div>
838 <div class="cat-programming">
839 <div class="big-title">
840 <span id="title-text">programming</span>
841 <div class="big-title-l"></div>
842 <div class="big-title-r"></div>
843 </div>
844 <div class="collection cat-content"></div>
845 </div>
846 <div class="cat-art">
847 <div class="big-title">
848 <span id="title-text">art</span>
849 <div class="big-title-l"></div>
850 <div class="big-title-r"></div>
851 </div>
852 <div class="collection cat-content"></div>
853 </div>
854 <div class="cat-misc">
855 <div class="big-title">
856 <span id="title-text">misc</span>
857 <div class="big-title-l"></div>
858 <div class="big-title-r"></div>
859 </div>
860 <div class="collection cat-content"></div>
861 </div>
862
863 <!-- 404 -->
864 <div class="oohps hide">
865 <div class="big-title">
866 <span id="title-text">404 :/</span>
867 <div class="big-title-l"></div>
868 <div class="big-title-r"></div>
869 </div>
870 <div class="simple-text">
871 <p style="text-align: center; color: var(--clr-green2); font-size: 1.5em;">
872 I'm afraid, that I'm not able to serve the desired page to you
873 </p>
874 <p style="text-align: center; color: var(--clr-green2); font-size: 1.7em;">
875 sorry :(
876 </p>
877 <noscript>
878 <p style="text-align: center; color: var(--clr-green2); font-size: 1.1em;">
879 Note: This 404 message is always displayed if you have Javascript disabled
880 </p>
881 </noscript>
882 </div>
883 </div>
884 </div>
885 <footer>
886 <p>:: :: ::</p>
887 </footer>
888 </div>
889 </div>
890 <script type="text/javascript" src="./data.js"></script>
891 <script>
892 function asleep(ms) {
893 return new Promise(resolve => setTimeout(resolve, ms));
894 }
895
896 function asleep_dyn(str_len) {
897 const sleep_time = Math.floor(str_len * 0.05 * 1000);
898 return new Promise(resolve => setTimeout(resolve, sleep_time));
899 }
900
901 function enc(string) {
902 let buf = '';
903 for (let i = string.length - 1; i >= 0; i-- ) {
904 buf += String.fromCharCode(string.charCodeAt(i) + 1)
905 }
906 return buf;
907 }
908
909 async function dec(string, ref) {
910 let buf = '';
911
912 for (c of string) {
913 buf += String.fromCharCode(c.charCodeAt(0) - 1);
914 ref.innerText = buf;
915 await asleep(50);
916 }
917
918 buf = buf.split('');
919 let buf2 = buf.toReversed();
920
921 for (let i = buf.length - 1; i >= 0; i--) {
922 buf[i] = buf2[i];
923 let t = buf.join('');
924 ref.innerText = t;
925 await asleep(50);
926 }
927 }
928
929 function anim_title(hash) {
930 let title_elem = hash.replace('#', '.');
931
932 if (!title_elem) {
933 return;
934 }
935
936 let collection = document.querySelector(title_elem);
937
938 if (!collection) {
939 return;
940 }
941
942 let coll_children = collection.children;
943
944 let title = Array.from(coll_children).forEach((e => {
945 let elem = e.querySelector('#title-text');
946
947 if (!elem) return;
948
949 let _ = new Promise(resolve => {
950 const text = elem.innerText;
951 const encoded = enc(text);
952 dec(encoded, elem);
953 });
954 }))
955 }
956
957 let PAUSE_BANNER = true;
958 let LOCK_BIGGIE = false;
959
960 function toggle_banner_animation() {
961 const toggle = document.querySelector('#pause-anim-btn');
962 const no_ads_row = document.querySelector('.no-ads-row');
963 no_ads_row.classList.toggle('anim-stop');
964
965 if (PAUSE_BANNER) {
966 PAUSE_BANNER = false;
967 toggle.textContent = '[ PAUSE BANNER ]';
968 return;
969 }
970
971 PAUSE_BANNER = true;
972 toggle.textContent = '[ START BANNER ]';
973 }
974
975 function render_entry(name, cat, url, descr) {
976 const outer_div = document.createElement('div');
977
978 if (cat) {
979 outer_div.classList.add('l-' + cat);
980 }
981
982 const anchor = document.createElement('a');
983 anchor.href = url;
984 anchor.target = '_blank';
985 anchor.textContent = name;
986
987 const inner_div = document.createElement('div');
988 inner_div.textContent = descr;
989
990 outer_div.appendChild(anchor);
991 outer_div.appendChild(inner_div);
992 return outer_div;
993 }
994
995 function render_full_collection() {
996 const randEntries = data.entries;
997
998 randEntries.forEach(entry => {
999 const { name, category, url, description } = entry;
1000 const outer_div = render_entry(name, category, url, description);
1001 const container = document.querySelector('#all-links');
1002 container.appendChild(outer_div);
1003 });
1004 }
1005
1006 function render_rnd_collection() {
1007 const container = document.querySelector('#random-collection');
1008
1009 while (container.firstChild) {
1010 container.removeChild(container.firstChild);
1011 }
1012
1013 const randEntries = data.entries
1014 .sort(() => 0.5 - Math.random())
1015 .slice(0, 10);
1016
1017 randEntries.forEach(entry => {
1018 const { name, category, url, description } = entry;
1019 const outer_div = render_entry(name, category, url, description);
1020 container.appendChild(outer_div);
1021 });
1022 }
1023
1024 function render_categories() {
1025 data.entries.forEach(entry => {
1026 const { name, category, url, description } = entry;
1027 const outer_div = render_entry(name, null, url, description);
1028 const container = document.querySelector('.cat-' + category).querySelector('.cat-content');
1029 container.appendChild(outer_div);
1030 })
1031 }
1032
1033 async function terminal_print(string, ref) {
1034 for (c of string) {
1035 ref.textContent += c;
1036 await asleep(50);
1037 }
1038 return string.length;
1039 }
1040
1041 async function story_biggie(progress, ref) {
1042 const lenny = [
1043 '??',
1044 'Yooo, stop clicking! (╯°□°)╯',
1045 'Staaaahp ò_ô',
1046 'Really? You want to push it even further? (ㆆ _ ㆆ)',
1047 'Alright then...',
1048 'carry on... (︶︹︶)',
1049 'Great! Now you butchered this site ಠ_ಠ',
1050 'and I have to programm a new one (ノಠ益ಠ)ノ彡┻━┻'
1051 ];
1052 function clear() {
1053 ref.replaceChildren();
1054 }
1055 function p() {
1056 const p = document.createElement('p');
1057 p.classList.add('blinky');
1058 child = ref.appendChild(p);
1059 return child;
1060 }
1061 async function scene(id) {
1062 LOCK_BIGGIE = true;
1063 const text_len = await terminal_print(lenny[id], p());
1064 await asleep_dyn(text_len);
1065 LOCK_BIGGIE = false;
1066 }
1067
1068 clear();
1069
1070 switch (progress) {
1071 case 10:
1072 await scene(0);
1073 break;
1074 case 20:
1075 await scene(1);
1076 break;
1077 case 40:
1078 await scene(2);
1079 break;
1080 case 60:
1081 await scene(3);
1082 break;
1083 case 70:
1084 await scene(4);
1085 clear();
1086 await scene(5);
1087 break;
1088 case 100:
1089 await scene(6);
1090 clear();
1091 await scene(7);
1092 LOCK_BIGGIE = true;
1093 break;
1094 }
1095 }
1096
1097 function make_it_bigger() {
1098 const biggie = document.querySelector('.make-it-bigger');
1099
1100 if (!biggie) {
1101 console.log('Biggie: Can not find div container');
1102 return;
1103 }
1104
1105 let first_click = false;
1106 let w = 0;
1107 let h = 0;
1108
1109 biggie.addEventListener('click', function() {
1110 if (!first_click) {
1111 first_click = true;
1112 biggie.style.animation = 'none';
1113 }
1114 if (!LOCK_BIGGIE) {
1115 w = w < 99 ? w + 10 : 100;
1116 h = h < 99 ? h + 10 : 100;
1117 biggie.style.width = w + 'vw';
1118 biggie.style.height = h + 'vh';
1119 story_biggie(w, biggie);
1120 }
1121 });
1122 }
1123
1124 function content_reset() {
1125 const other_divs = document.querySelectorAll('.content > div');
1126 other_divs.forEach(section => section.classList.add('hide'));
1127 }
1128
1129 function router(hash) {
1130 let route = hash.replace('#', '');
1131 content_reset();
1132
1133 if (!route) {
1134 route = 'random'; // default page
1135 }
1136
1137 const target_div = document.querySelector('.' + route);
1138
1139 if (!target_div) {
1140 console.log('Error: Missing entry in content: ' + route);
1141 document.querySelector('.oohps').classList.remove('hide');
1142 return;
1143 }
1144
1145 target_div.classList.remove('hide');
1146 }
1147
1148 window.addEventListener("hashchange", function () {
1149 const route = location.hash;
1150 router(route);
1151 anim_title(route);
1152 })
1153
1154 document.addEventListener("keydown", function(event) {
1155 if (event.key === 'F4') {
1156 render_rnd_collection();
1157 }
1158 })
1159
1160 document.addEventListener("DOMContentLoaded", function () {
1161 const route = window.location.hash;
1162 render_rnd_collection();
1163 render_categories();
1164 render_full_collection();
1165 router(route);
1166 anim_title(route);
1167 make_it_bigger();
1168 toggle_banner_animation(); // Initial: animation off; in case someone has no js enabled
1169 })
1170 </script>
1171</body>
1172</html>
1173
Copyright 2026  E766CB298A6D1E64 | Git-Thing heavily inspired by cgit