├── scrot.webp
├── assets
├── favicon
│ ├── favicon.ico
│ ├── favicon-16.png
│ ├── favicon-32.png
│ ├── favicon-57.png
│ ├── favicon-60.png
│ ├── favicon-64.png
│ ├── favicon-70.png
│ ├── favicon-72.png
│ ├── favicon-76.png
│ ├── favicon-96.png
│ ├── favicon-114.png
│ ├── favicon-120.png
│ ├── favicon-144.png
│ ├── favicon-150.png
│ ├── favicon-152.png
│ ├── favicon-160.png
│ ├── favicon-180.png
│ ├── favicon-192.png
│ ├── favicon-310.png
│ ├── browserconfig.xml
│ └── faviconit-instructions.txt
├── webcons
│ ├── office365.svg
│ ├── unsplash.svg
│ ├── deviantart.svg
│ ├── materialio.svg
│ ├── stackoverflow.svg
│ ├── markdown.svg
│ ├── youtube.svg
│ ├── stackexchange.svg
│ ├── gitlab.svg
│ ├── facebook.svg
│ ├── startpage.svg
│ ├── flaticon.svg
│ ├── figma.svg
│ ├── mega.svg
│ ├── github.svg
│ ├── netflix.svg
│ ├── twitter.svg
│ ├── bitbucket.svg
│ ├── messenger.svg
│ ├── discord.svg
│ ├── reddit.svg
│ ├── spotify.svg
│ ├── superuser.svg
│ ├── google.svg
│ ├── soundcloud.svg
│ ├── 4chan.svg
│ ├── twitch.svg
│ ├── gdrive.svg
│ ├── instagram.svg
│ ├── gmail.svg
│ ├── yahoo.svg
│ ├── commons.svg
│ ├── jsfiddle.svg
│ ├── wikipedia.svg
│ ├── interneting-is-hard.svg
│ ├── ecosia.svg
│ ├── ebay.svg
│ ├── amazon.svg
│ └── calendar.svg
├── buttons
│ ├── categories.svg
│ ├── theme-switch.svg
│ ├── search-engine.svg
│ └── alphabetical.svg
├── search-engines
│ ├── startpage.svg
│ ├── google.svg
│ ├── yahoo.svg
│ └── ecosia.svg
└── theme-buttons
│ ├── light-mode.svg
│ └── dark-mode.svg
├── css
├── style.css
├── panel-button.css
├── suggestions.css
├── global.css
├── animated-background.css
├── central-body.css
├── panel-body.css
└── web-menu.css
├── js
├── central-body.js
├── send-url-params.js
├── search-box-key-events.js
├── document-key-events.js
├── script.js
├── search-query-send.js
├── clock.js
├── search-engine-switcher.js
├── theme-switcher.js
├── panel-buttons.js
└── suggestions.js
├── LICENSE
├── index.html
└── README.md
/scrot.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/scrot.webp
--------------------------------------------------------------------------------
/assets/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon.ico
--------------------------------------------------------------------------------
/assets/favicon/favicon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-16.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-32.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-57.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-60.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-64.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-70.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-72.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-76.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-96.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-114.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-120.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-144.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-150.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-152.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-160.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-160.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-180.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-192.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eromatiya/squareup/HEAD/assets/favicon/favicon-310.png
--------------------------------------------------------------------------------
/assets/webcons/office365.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | /* Load all CSS */
2 | @import url('normalize.css');
3 | @import url('global.css');
4 | @import url('animated-background.css');
5 | @import url('central-body.css');
6 | @import url('suggestions.css');
7 | @import url('panel-body.css');
8 | @import url('panel-button.css');
9 | @import url('web-menu.css');
10 |
--------------------------------------------------------------------------------
/js/central-body.js:
--------------------------------------------------------------------------------
1 | class CentralBody {
2 | constructor() {
3 | this._centralBody = document.querySelector('#central-body');
4 | }
5 |
6 | hideCentralBody() {
7 | this._centralBody.classList.add('central-body-hide');
8 | }
9 |
10 | showCentralBody() {
11 | this._centralBody.classList.remove('central-body-hide');
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/assets/favicon/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | #FFFFFF
9 |
10 |
11 |
--------------------------------------------------------------------------------
/js/send-url-params.js:
--------------------------------------------------------------------------------
1 | class ParamQuerySend {
2 | constructor() {
3 | this._searchBox = document.querySelector('#search-box');
4 | this._loadQueryParam();
5 | }
6 |
7 | _loadQueryParam() {
8 | // Accepts URL query
9 | let paramString = window.location.search;
10 | let queryString = new URLSearchParams(String(paramString));
11 | let q = queryString.has('q');
12 | if (q) this._paramSendQuery(queryString.get('q'));
13 | }
14 |
15 | _paramSendQuery(param) {
16 | this._searchBox.value = String(param);
17 | searchQuerySend.sendQuery();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/js/search-box-key-events.js:
--------------------------------------------------------------------------------
1 | class SearchBoxKeyEvents {
2 | constructor() {
3 | this._searchBox = document.querySelector('#search-box');
4 | this._registerSearchBoxOnKeyUpEvent();
5 | }
6 |
7 | _registerSearchBoxOnKeyUpEvent() {
8 |
9 | this._searchBox.addEventListener(
10 | 'keyup',
11 | e => {
12 | e.preventDefault();
13 |
14 | // Fail safe device
15 | if (!this._searchBox) {
16 | this._searchBox = document.querySelector('#search-box');
17 | }
18 |
19 | if (e.key === 'Tab') return;
20 |
21 | if (e.key.length === 1 || e.key === 'Backspace') {
22 | if (this._searchBox.value < 1) {
23 | autoSuggestion.hideSuggestions();
24 | return;
25 | }
26 |
27 | // Fetch suggestion
28 | autoSuggestion.fetchSuggestions();
29 | return;
30 | }
31 |
32 | // Search query
33 | if (e.key === 'Enter') {
34 |
35 | // Don't accept empty strings
36 | if (this._searchBox.value < 1) {
37 | return;
38 | }
39 |
40 | searchQuerySend.sendQuery();
41 | }
42 | }
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/js/document-key-events.js:
--------------------------------------------------------------------------------
1 | class DocumentKeyEvents {
2 | constructor() {
3 | this._keysLog = {};
4 | this._keyUpEvent();
5 | this._keyDownEvent();
6 | }
7 |
8 | _keyUpEvent() {
9 | document.addEventListener(
10 | 'keyup',
11 | e => {
12 | // Prevent default escape key function
13 | e.preventDefault();
14 |
15 | // Toggle web menu on escape button
16 | if (e.key === 'Escape') {
17 | webMenu.toggleWebMenu();
18 | return;
19 | }
20 |
21 | // Switch search engine
22 | if (this._keysLog['Control'] && e.code === 'Space') {
23 | e.preventDefault();
24 | searchEngineSwitcher.searchEngineSwitch();
25 | return;
26 | }
27 |
28 | // Switch color scheme
29 | if (this._keysLog['Alt'] && e.code === 'Space') {
30 | e.preventDefault();
31 | themeSwitcher.themeSwitch();
32 | return;
33 | }
34 |
35 | delete this._keysLog[e.key];
36 | }
37 | );
38 | }
39 |
40 | _keyDownEvent() {
41 | document.addEventListener(
42 | 'keydown',
43 | e => {
44 | this._keysLog[e.key] = true;
45 | }
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/css/panel-button.css:
--------------------------------------------------------------------------------
1 | /* Panel Button */
2 | .panel-button {
3 | background: transparent;
4 | width: 48px;
5 | height: 48px;
6 | position: relative;
7 | border-radius: 6px;
8 | border: none;
9 | cursor: pointer;
10 | transition: transform 0.2s;
11 | }
12 |
13 | .panel-button.active-content {
14 | background: var(--button-active);
15 | }
16 |
17 | .panel-button:hover {
18 | background: var(--button-hover);
19 | transform: scale(1.25);
20 | }
21 |
22 | .panel-button:active {
23 | background: var(--button-active);
24 | transform: scale(1);
25 | }
26 |
27 | .panel-button-image {
28 | background-size: cover;
29 | }
30 |
31 | /* Panel Link - a href */
32 | .panel-link {
33 | display: block;
34 | text-decoration: none;
35 | outline: 0;
36 | border: none;
37 | outline-style: none;
38 | user-select: none;
39 | -webkit-user-drag: none;
40 | }
41 |
42 | .panel-button div {
43 | background-size: cover;
44 | width: 24px;
45 | height: 24px;
46 | border: none;
47 | position: absolute;
48 | left: 0;
49 | right: 0;
50 | top: 0;
51 | bottom: 0;
52 | margin: auto;
53 | max-width: 100%;
54 | max-height: 100%;
55 | }
56 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Gerome Matilla
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/js/script.js:
--------------------------------------------------------------------------------
1 | // Instantiate all js scripts
2 | // Heirarchy is important here to avoid temporal dead zones
3 |
4 | // Instantiate config
5 | const config = new Config();
6 |
7 | // Instantiate a clock object
8 | const clock = new Clock();
9 |
10 | // Instantiate panel buttons
11 | const panelButtons = new PanelButtons();
12 |
13 | // Instantiate central body
14 | const centralBody = new CentralBody();
15 |
16 | // Instantiate search box key events
17 | const searchBoxKeyEvents = new SearchBoxKeyEvents();
18 |
19 | // Instantiate search query send
20 | const searchQuerySend = new SearchQuerySend();
21 |
22 | // Instantiate autosuggestion
23 | const autoSuggestion = new AutoSuggestion();
24 |
25 | // Instantiate search engine settings
26 | const searchEngineSwitcher = new SearchEngineSwitcher();
27 |
28 | // Instantiate autosuggestion
29 | const paramQuerySend = new ParamQuerySend();
30 |
31 | // Instantantiate web menu
32 | const webMenu = new WebMenu();
33 |
34 | // Instantiate theme switcher
35 | const themeSwitcher = new ThemeSwitcher();
36 |
37 | // Instantiate key events
38 | const documentKeyEvents = new DocumentKeyEvents();
39 |
40 | // Set Font Family
41 | document.querySelector('body').style.setProperty(
42 | 'font-family', config.getFontFamily()['sans-serif']
43 | );
--------------------------------------------------------------------------------
/css/suggestions.css:
--------------------------------------------------------------------------------
1 | #suggestions-container {
2 | background-color: var(--base-bg);
3 | position: absolute;
4 | width: 100%;
5 | height: auto;
6 | margin-top: 5px;
7 | border-radius: 24px;
8 | display: none;
9 | box-shadow:
10 | 0 2.8px 2.2px rgba(0, 0, 0, 0.034),
11 | 0 6.7px 5.3px rgba(0, 0, 0, 0.048),
12 | 0 12.5px 10px rgba(0, 0, 0, 0.06),
13 | 0 22.3px 17.9px rgba(0, 0, 0, 0.072),
14 | 0 41.8px 33.4px rgba(0, 0, 0, 0.086),
15 | 0 100px 80px rgba(0, 0, 0, 0.12);
16 | user-select: none;
17 | }
18 |
19 | #suggestions-container.suggestion-show {
20 | display: block;
21 | }
22 |
23 | ul#suggestions {
24 | list-style: none !important;
25 | padding: 0;
26 | }
27 |
28 | li.suggestion {
29 | padding: 0;
30 | list-style: none;
31 | width: 100%;
32 | }
33 |
34 | li button.suggestion-button {
35 | background: transparent;
36 | color: var(--font-color);
37 | font-size: 12pt;
38 | border: none;
39 | width: auto;
40 | margin: 0;
41 | padding: 2px 0;
42 | width: 100%;
43 | outline: none;
44 | overflow-y: auto;
45 | }
46 |
47 | li button.suggestion-button:hover {
48 | background: var(--button-hover);
49 | }
50 |
51 | li button.suggestion-button:active {
52 | background: var(--button-active);
53 | }
54 |
55 | li button.suggestion-button:focus {
56 | background: var(--button-active);
57 | }
58 |
--------------------------------------------------------------------------------
/js/search-query-send.js:
--------------------------------------------------------------------------------
1 | class SearchQuerySend {
2 | constructor() {
3 | this._searchBox = document.querySelector('#search-box');
4 | this._quickSearchData = config.getQuickSearchData();
5 | }
6 |
7 | // Is query a valid URL
8 | _isURL(u) {
9 | let dummyInput = document.createElement('input');
10 | dummyInput.setAttribute('type', 'url');
11 | dummyInput.value = u;
12 | return dummyInput.validity.valid;
13 | }
14 |
15 | // Open link
16 | _openURL(url) {
17 | window.location.href = encodeURI(url);
18 | }
19 |
20 | // Quick search
21 | _quickSearch(query) {
22 | const prefix = query.substring(0, query.indexOf('/') + 1);
23 |
24 | // Checks if it's a valid quick search
25 | if (typeof this._quickSearchData[String(prefix)] === 'undefined') {
26 | return false;
27 | } else {
28 | const webSite = this._quickSearchData[String(prefix)].urlPrefix;
29 | const queryNoSuffix = query.substring(prefix.indexOf('/') + 1);
30 | this._openURL(webSite + queryNoSuffix);
31 | return true;
32 | }
33 | }
34 |
35 | // Search query
36 | sendQuery() {
37 | const searchQuery = this._searchBox.value;
38 |
39 | if (this._isURL(searchQuery)) {
40 | this._openURL(searchQuery);
41 | return;
42 | }
43 |
44 | if (this._quickSearch(searchQuery)) {
45 | return;
46 | }
47 |
48 | this._openURL(searchEngineSwitcher.getSearchEngineURLPrefix() + searchQuery);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/js/clock.js:
--------------------------------------------------------------------------------
1 | class Clock {
2 | constructor() {
3 | this._localStorage = window.localStorage;
4 | this._clock = document.querySelector('#clock');
5 | this._setTime = this._setTime.bind(this);
6 | this._twentyFourMode = false;
7 | this._clockUpdater = null;
8 | this._init();
9 | }
10 |
11 | _appendZero(k) {
12 | // Append zero if k < 10
13 | return k = (k < 10) ? '0' + k : k;
14 | }
15 |
16 | _setTime() {
17 | const date = new Date();
18 | let hour = date.getHours();
19 | let min = date.getMinutes();
20 | let midDay = null;
21 | min = this._appendZero(min);
22 |
23 | // 24-hour mode
24 | if (this._twentyFourMode === true) {
25 | hour = this._appendZero(hour);
26 | this._clock.innerText = `${hour}:${min}`;
27 | return;
28 | }
29 |
30 | // 12-hour mode
31 | midDay = (hour >= 12) ? 'PM' : 'AM';
32 | hour = (hour === 0) ? 12 : ((hour > 12) ? (hour - 12) : hour);
33 | hour = this._appendZero(hour);
34 | this._clock.innerText = `${hour}:${min} ${midDay}`;
35 | }
36 |
37 | _startClock() {
38 | this._setTime();
39 | this._clockUpdater = setInterval(this._setTime, 1000);
40 | }
41 |
42 | _updateClockMode() {
43 | clearInterval(this._clockUpdater);
44 | this._twentyFourMode = !this._twentyFourMode;
45 | this._localStorage.setItem('twentyFourMode', JSON.stringify(this._twentyFourMode));
46 | this._startClock();
47 | }
48 |
49 | _clockClickEvent() {
50 | this._clock.addEventListener(
51 | 'click',
52 | () => {
53 | console.log('toggle 24-hour clock mode');
54 | this._updateClockMode();
55 | }
56 | );
57 | }
58 |
59 | _init() {
60 | this._twentyFourMode = JSON.parse(this._localStorage.getItem('twentyFourMode')) || false;
61 | this._startClock();
62 | this._clockClickEvent();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/assets/favicon/faviconit-instructions.txt:
--------------------------------------------------------------------------------
1 | thanks for using faviconit!
2 | copy the files to your site and add this code inside the HTML
tag:
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/assets/webcons/unsplash.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
57 |
58 |
--------------------------------------------------------------------------------
/assets/webcons/deviantart.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
29 |
31 |
52 |
57 |
58 |
--------------------------------------------------------------------------------
/assets/buttons/categories.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
29 |
31 |
52 |
56 |
57 |
--------------------------------------------------------------------------------
/css/global.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --base-bg: #FFFFFF;
3 | --base-accent: #92B6F4;
4 | --base-secondary-bg: #EFEFEF;
5 | --boxes-bg: var(--base-bg);
6 | --font-color: #0A0A0A;
7 | --clock-color: #FFFFFF;
8 | --hover-bg: #CDCDCD;
9 | --active-bg: var(--base-accent);
10 | --button-hover: var(--hover-bg);
11 | --button-active: var(--active-bg);
12 | --gradient-start-bg: #4568dc;
13 | --gradient-end-bg: #b06ab3;
14 | --transition-speed: 300ms;
15 | --image-invert: invert(0);
16 | }
17 |
18 | [data-theme='dark'] {
19 | --base-bg: #1A1A1A;
20 | --base-accent: #EE4F84;
21 | --base-secondary-bg: #252525;
22 | --boxes-bg: var(--base-bg);
23 | --font-color: #F2F2F2;
24 | --clock-color: #0A0A0A;
25 | --hover-bg: #353535;
26 | --active-bg: var(--base-accent);
27 | --button-hover: var(--hover-bg);
28 | --button-active: var(--active-bg);
29 | --gradient-start-bg: #7b4397;
30 | --gradient-end-bg: #dc2430;
31 | --image-invert: invert(1);
32 | }
33 |
34 | * {
35 | box-sizing: border-box;
36 | -webkit-font-smoothing: antialiased;
37 | -moz-osx-font-smoothing: grayscale;
38 | text-rendering: optimizeLegibility;
39 | font-variant-ligatures: none;
40 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
41 | }
42 |
43 | html,
44 | body {
45 | margin: 0;
46 | padding: 0;
47 | height: 100%;
48 | position: fixed;
49 | }
50 |
51 | body {
52 | top: 0;
53 | left: 0;
54 | width: 100%;
55 | height: 100%;
56 | margin: 0;
57 | padding: 0;
58 | overflow: hidden;
59 | background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
60 | }
61 |
62 | /* Remove outline on input fields on chrome */
63 | input:focus,
64 | textarea:focus,
65 | select:focus {
66 | outline: none;
67 | }
68 |
69 | /* Placeholder */
70 | ::placeholder {
71 | color: var(--font-color);
72 | opacity: 0.65;
73 | }
74 |
75 | option {
76 | text-align: left;
77 | }
78 |
79 | img {
80 | user-select: none;
81 | }
82 |
83 | /* Scrollbar */
84 | ::-webkit-scrollbar {
85 | width: 10px;
86 | }
87 |
88 | ::-webkit-scrollbar-track {
89 | background: var(--base-bg);
90 | }
91 |
92 | ::-webkit-scrollbar-thumb {
93 | background: var(--base-secondary-bg);
94 | }
95 |
96 | ::-webkit-scrollbar-thumb:hover {
97 | background: var(--hover-bg);
98 | }
99 |
--------------------------------------------------------------------------------
/assets/webcons/materialio.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 | ic_material_192px_light
26 |
27 |
28 |
29 |
31 |
52 | ic_material_192px_light
54 |
57 |
63 |
67 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/css/animated-background.css:
--------------------------------------------------------------------------------
1 | .gradient-body {
2 | width: 100%;
3 | height: 100vh;
4 | background: linear-gradient(
5 | 45deg,
6 | var(--gradient-start-bg),
7 | var(--gradient-end-bg)
8 | );
9 | background-size: 400% 400%;
10 | animation: wave 20s ease infinite;
11 | }
12 |
13 | @keyframes wave {
14 | 0% {
15 | background-position: 0% 81%
16 | }
17 |
18 | 50% {
19 | background-position: 100% 20%
20 | }
21 |
22 | 100% {
23 | background-position: 0% 81%
24 | }
25 | }
26 |
27 | .gradient-body div {
28 | background: var(--boxes-bg);
29 | height: 60px;
30 | width: 60px;
31 | position: absolute;
32 | top: 10%;
33 | left: 10%;
34 | border-radius: 15px;
35 | animation: float 4s linear infinite;
36 | }
37 |
38 | .gradient-body div:nth-child(1) {
39 | top: 20%;
40 | left: 20%;
41 | animation: float 8s linear infinite;
42 | }
43 |
44 | .gradient-body div:nth-child(2) {
45 | top: 26%;
46 | left: 89%;
47 | animation: float 10s linear infinite;
48 | }
49 |
50 | .gradient-body div:nth-child(3) {
51 | top: 80%;
52 | left: 90%;
53 | animation: float 5s linear infinite;
54 | }
55 |
56 | .gradient-body div:nth-child(4) {
57 | top: 65%;
58 | left: 75%;
59 | animation: float 7s linear infinite;
60 | }
61 |
62 | .gradient-body div:nth-child(5) {
63 | top: 90%;
64 | left: 10%;
65 | animation: float 9s linear infinite;
66 | }
67 |
68 | .gradient-body div:nth-child(6) {
69 | top: 30%;
70 | left: 60%;
71 | animation: float 5s linear infinite;
72 | }
73 |
74 | .gradient-body div:nth-child(7) {
75 | top: 70%;
76 | left: 33%;
77 | animation: float 8s linear infinite;
78 | }
79 |
80 | .gradient-body div:nth-child(8) {
81 | top: 75%;
82 | left: 60%;
83 | animation: float 10s linear infinite;
84 | }
85 |
86 | .gradient-body div:nth-child(9) {
87 | top: 23%;
88 | left: 50%;
89 | animation: float 6s linear infinite;
90 | }
91 |
92 | .gradient-body div:nth-child(10) {
93 | top: 35%;
94 | left: 7%;
95 | animation: float 10s linear infinite;
96 | }
97 |
98 | @keyframes float {
99 | 0% {
100 | transform: scale(0) translateY(0) translateZ(0) rotate(50deg);
101 | }
102 |
103 | 100% {
104 | transform: scale(1.6) translateY(-250px) translateZ(0) rotate(360deg);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/assets/webcons/stackoverflow.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
29 |
31 |
52 |
54 |
57 |
61 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/assets/webcons/markdown.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
29 |
31 |
52 |
56 |
57 |
--------------------------------------------------------------------------------
/css/central-body.css:
--------------------------------------------------------------------------------
1 | #central-body {
2 | position: absolute;
3 | top: 50%;
4 | left: 50%;
5 | z-index: 10;
6 | opacity: 1;
7 | transform: translate(-50%,-50%);
8 | transition:
9 | opacity var(--transition-speed),
10 | z-index var(--transition-speed);
11 | }
12 |
13 | #central-body.central-body-hide {
14 | z-index: 0;
15 | opacity: 0;
16 | }
17 |
18 | #clock {
19 | font-weight: bold;
20 | font-size: 88pt;
21 | color: var(--clock-color);
22 | white-space: nowrap;
23 | text-align: center;
24 | margin-bottom: 40px;
25 | text-shadow: 2px 2px #0A0A0A22;
26 | user-select: none;
27 | transition: font-size var(--transition-speed);
28 | }
29 |
30 | #search-container {
31 | width: 100%;
32 | }
33 |
34 | #search-box {
35 | background: var(--base-bg);
36 | color: var(--font-color);
37 | font-size: 14pt;
38 | text-align: center;
39 | position: relative;
40 | left: 50%;
41 | transform: translateX(-50%);
42 | padding: 10px;
43 | border: none;
44 | border-radius: 24px;
45 | box-shadow:
46 | 0 2.8px 2.2px rgba(0, 0, 0, 0.034),
47 | 0 6.7px 5.3px rgba(0, 0, 0, 0.048),
48 | 0 12.5px 10px rgba(0, 0, 0, 0.06),
49 | 0 22.3px 17.9px rgba(0, 0, 0, 0.072),
50 | 0 41.8px 33.4px rgba(0, 0, 0, 0.086),
51 | 0 100px 80px rgba(0, 0, 0, 0.12);
52 | max-width: 100%;
53 | transition:
54 | max-width var(--transition-speed),
55 | font-size var(--transition-speed);
56 | }
57 |
58 | @media screen and (max-width: 550px) {
59 | #search-box {
60 | max-width: 70%;
61 | font-size: 14pt;
62 | }
63 |
64 | #clock {
65 | font-size: 70pt;
66 | }
67 | }
68 |
69 | @media screen and (max-width: 530px) {
70 | #search-box {
71 | max-width: 65%;
72 | font-size: 14pt;
73 | }
74 |
75 | #clock {
76 | font-size: 64pt;
77 | }
78 | }
79 |
80 | @media screen and (max-width: 515px) {
81 | #search-box {
82 | max-width: 60%;
83 | font-size: 14pt;
84 | }
85 |
86 | #clock {
87 | font-size: 48pt;
88 | }
89 | }
90 |
91 | @media screen and (max-width: 400px) {
92 | #search-box {
93 | max-width: 55%;
94 | font-size: 12pt;
95 | }
96 |
97 | #clock {
98 | font-size: 32pt;
99 | }
100 | }
101 |
102 | @media screen and (max-width: 360px) {
103 | #search-box {
104 | font-size: 11pt;
105 | }
106 |
107 | #clock {
108 | font-size: 30pt;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/assets/webcons/youtube.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
58 |
59 |
--------------------------------------------------------------------------------
/js/search-engine-switcher.js:
--------------------------------------------------------------------------------
1 | class SearchEngineSwitcher {
2 | constructor() {
3 | this._localStorage = window.localStorage;
4 | this._searchBox = document.querySelector('#search-box');
5 | this._buttonSearchEngine = document.querySelector('#button-search-engine');
6 | this._buttonImageSearchEngine = document.querySelector('#button-image-search-engine');
7 |
8 | this._searchEngines = config.getSearchEngines();
9 | this._searchEnginesArr = [];
10 | this._searchEnginesIndex = 0;
11 | this._activeSearchEngine = '';
12 | this._activeSearchEnginePrefix = '';
13 |
14 | this._init()
15 | }
16 |
17 | _createSearchEngineList() {
18 | Object.keys(this._searchEngines).forEach(
19 | key => {
20 | this._searchEnginesArr.push(key);
21 | }
22 | );
23 | }
24 |
25 | getSearchEngineURLPrefix() {
26 | return this._activeSearchEnginePrefix;
27 | }
28 |
29 | _updateSearchEngine() {
30 | this._activeSearchEngine = this._searchEnginesArr[String(this._searchEnginesIndex)];
31 | let searchEngineObject = this._searchEngines[String(this._activeSearchEngine)];
32 |
33 | this._activeSearchEnginePrefix = searchEngineObject.prefix;
34 | let searchEngineName = searchEngineObject.name;
35 | let searchEngineIcon = searchEngineObject.icon;
36 |
37 | this._buttonImageSearchEngine.style.setProperty('background-image', `url('assets/search-engines/${searchEngineIcon}.svg')`);
38 | this._buttonImageSearchEngine.style.setProperty('background-size', 'cover');
39 | this._searchBox.placeholder = `Search with ${searchEngineName}`;
40 | }
41 |
42 | _incrementSearchEngineIndex() {
43 | this._searchEnginesIndex = (this._searchEnginesIndex + 1) % this._searchEnginesArr.length;
44 | }
45 |
46 | searchEngineSwitch() {
47 | this._updateSearchEngine();
48 | this._localStorage.setItem('searchEngine', this._activeSearchEngine);
49 | this._incrementSearchEngineIndex();
50 | }
51 |
52 | _buttonSearchEngineClickEvent() {
53 | this._buttonSearchEngine.addEventListener(
54 | 'click',
55 | () => {
56 | this.searchEngineSwitch();
57 | }
58 | )
59 | }
60 |
61 | _init() {
62 | this._createSearchEngineList();
63 | this._activeSearchEngine = this._localStorage.getItem('searchEngine') ||
64 | this._searchEnginesArr[parseInt(0, 10)];
65 | this._searchEnginesIndex = this._searchEnginesArr.indexOf(this._activeSearchEngine);
66 | this._updateSearchEngine();
67 | this._incrementSearchEngineIndex();
68 | this._buttonSearchEngineClickEvent();
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/assets/webcons/stackexchange.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
29 |
31 |
52 |
54 |
57 |
61 |
65 |
69 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/assets/webcons/gitlab.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
29 |
31 |
52 |
55 |
59 |
63 |
67 |
71 |
75 |
79 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/assets/webcons/facebook.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
29 |
31 |
52 |
55 |
59 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/assets/webcons/startpage.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
41 |
43 | Created by potrace 1.16, written by Peter Selinger 2001-2019
44 |
45 |
47 | image/svg+xml
48 |
50 |
51 |
52 |
53 |
54 |
57 |
65 |
69 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/assets/search-engines/startpage.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
41 |
43 | Created by potrace 1.16, written by Peter Selinger 2001-2019
44 |
45 |
47 | image/svg+xml
48 |
50 |
51 |
52 |
53 |
54 |
57 |
65 |
69 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/assets/webcons/flaticon.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
56 |
60 |
64 |
67 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/assets/theme-buttons/light-mode.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
40 |
42 |
43 |
45 | image/svg+xml
46 |
48 |
49 |
50 |
51 |
52 |
57 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/assets/webcons/figma.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 | Figma.logo
26 |
27 |
28 |
29 |
31 |
52 |
54 | Figma.logo
56 | Created using Figma
58 |
61 |
65 |
69 |
73 |
77 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/assets/webcons/mega.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
54 |
--------------------------------------------------------------------------------
/js/theme-switcher.js:
--------------------------------------------------------------------------------
1 | class ThemeSwitcher {
2 | constructor() {
3 | this._localStorage = window.localStorage;
4 | this._buttonThemeSwitch = document.querySelector('#button-theme-switch');
5 | this._buttonImageThemeSwitch = document.querySelector('#button-image-theme-switch');
6 | this._themeModes = config.getThemeMode();
7 | this._themeModesArr = [];
8 | this._themeModesArrIndex = 0;
9 | this._activeThemeMode = '';
10 | this._init()
11 | }
12 |
13 | _createThemeModeList() {
14 | Object.keys(this._themeModes).forEach(
15 | key => {
16 | this._themeModesArr.push(key);
17 | }
18 | );
19 | }
20 |
21 | _incrementThemeModeIndex() {
22 | // Increment index while preventing it to go off limits
23 | this._themeModesArrIndex = (this._themeModesArrIndex + 1) % this._themeModesArr.length;
24 | }
25 |
26 | _applyThemeColors(themeLightHour, themeLightDark) {
27 |
28 | // Apply CSS Colors based on the active theme
29 | if (this._activeThemeMode == 'dark') {
30 | document.documentElement.setAttribute('data-theme', 'dark');
31 | } else if (this._activeThemeMode == 'light') {
32 | document.documentElement.setAttribute('data-theme', 'light');
33 | } else {
34 | const date = new Date();
35 | const hour = date.getHours();
36 | if (hour >= themeLightHour && hour < themeLightDark) {
37 | document.documentElement.setAttribute('data-theme', 'light');
38 | } else {
39 | document.documentElement.setAttribute('data-theme', 'dark');
40 | }
41 | }
42 | }
43 |
44 | _updateThemeMode() {
45 | this._activeThemeMode = this._themeModesArr[this._themeModesArrIndex];
46 | let themeObject = this._themeModes[String(this._activeThemeMode)];
47 | let themeName = themeObject.name;
48 | let themeIcon = themeObject.icon;
49 | let themeLightHour = 0;
50 | let themeLightDark = 0;
51 |
52 | if (this._activeThemeMode === 'auto') {
53 | themeLightHour = themeObject.lightHour;
54 | themeLightDark = themeObject.darkHour;
55 | }
56 |
57 | this._buttonImageThemeSwitch.style.setProperty('background-image', `url('assets/theme-buttons/${themeIcon}.svg')`);
58 | this._buttonImageThemeSwitch.style.setProperty('background-size', 'cover');
59 | this._applyThemeColors(themeLightHour, themeLightDark);
60 | }
61 |
62 | themeSwitch() {
63 | this._updateThemeMode();
64 | this._localStorage.setItem('themeMode', this._activeThemeMode);
65 | this._incrementThemeModeIndex();
66 | }
67 |
68 | _buttonThemeSwitchClickEvent() {
69 | this._buttonThemeSwitch.addEventListener(
70 | 'click',
71 | () => {
72 | this.themeSwitch();
73 | }
74 | );
75 | }
76 |
77 | _init() {
78 | this._createThemeModeList();
79 | this._activeThemeMode = this._localStorage.getItem('themeMode') ||
80 | this._themeModesArr[parseInt(0, 10)];
81 | this._themeModesArrIndex = this._themeModesArr.indexOf(this._activeThemeMode);
82 | this._updateThemeMode();
83 | this._incrementThemeModeIndex();
84 | this._buttonThemeSwitchClickEvent();
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/assets/webcons/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
54 |
59 |
60 |
--------------------------------------------------------------------------------
/assets/webcons/netflix.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
78 |
80 |
82 |
84 |
86 |
88 |
90 |
92 |
94 |
96 |
98 |
100 |
102 |
104 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/assets/webcons/twitter.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
44 |
48 |
50 |
51 |
53 |
54 |
56 |
57 |
59 |
60 |
62 |
63 |
65 |
66 |
68 |
69 |
71 |
72 |
74 |
75 |
77 |
78 |
80 |
81 |
83 |
84 |
86 |
87 |
89 |
90 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/js/panel-buttons.js:
--------------------------------------------------------------------------------
1 | class PanelButtons {
2 | constructor() {
3 | this._panel = document.querySelector('#panel-body');
4 | this._panelSites = config.getPanelSites();
5 | this._populatePanel();
6 | }
7 |
8 | _buildPanelButton(id, className, callback = null) {
9 | const panelButton = document.createElement('div');
10 | panelButton.id = `button-${id}`;
11 | panelButton.className = className;
12 | if (callback) panelButton.addEventListener('click', () => {callback();});
13 | return panelButton;
14 | }
15 |
16 | _buildPanelButtonImage(id, className, background) {
17 | const buttonImage = document.createElement('div');
18 | buttonImage.id = id;
19 | buttonImage.className = className;
20 | buttonImage.style.setProperty('background-image', background);
21 | return buttonImage;
22 | }
23 |
24 | _generateFromManual(id, icon, callback) {
25 |
26 | const panelButton = this._buildPanelButton(
27 | `${id}`,
28 | 'panel-button',
29 | callback
30 | );
31 |
32 | const buttonImage = this._buildPanelButtonImage(
33 | `button-image-${id}`,
34 | 'panel-button-image',
35 | `url('assets/buttons/${icon}.svg')`
36 | );
37 |
38 | panelButton.appendChild(buttonImage);
39 | this._panel.appendChild(panelButton);
40 | }
41 |
42 | _generateFromList() {
43 | for (let i = 0; i < (this._panelSites.length); i++) {
44 |
45 | const site = this._panelSites[parseInt(i, 10)].site;
46 | const icon = this._panelSites[parseInt(i, 10)].icon;
47 | const url = this._panelSites[parseInt(i, 10)].url;
48 |
49 | // Create an href
50 | const panelLink = document.createElement('a');
51 | panelLink.className = 'panel-link';
52 | panelLink.href = url;
53 | panelLink.tabIndex = '-1';
54 |
55 | // Create div container
56 | const panelButton = this._buildPanelButton(
57 | site,
58 | 'panel-button'
59 | );
60 |
61 | // Create div container for button icon
62 | const buttonImage = this._buildPanelButtonImage(
63 | `button-image-${i}`,
64 | 'panel-button-image',
65 | `url('assets/webcons/${icon}.svg')`
66 | );
67 |
68 | // Append divs
69 | panelButton.appendChild(buttonImage);
70 | panelLink.appendChild(panelButton);
71 | this._panel.appendChild(panelLink);
72 | }
73 | }
74 |
75 | _populatePanel() {
76 | // Create theme switcher
77 | this._generateFromManual(
78 | 'web-menu',
79 | 'web-menu',
80 | () => {
81 | // Look web-menu.js
82 | console.log('Toggle web menu');
83 | }
84 | );
85 |
86 | // Create web shortcuts
87 | this._generateFromList();
88 |
89 | // Create search engine switcher button
90 | this._generateFromManual(
91 | 'search-engine',
92 | 'search-engine',
93 | () => {
94 | // Look search-engine-settings.js
95 | console.log('Switch search engine');
96 | }
97 | );
98 |
99 | // Create theme switcher
100 | this._generateFromManual(
101 | 'theme-switch',
102 | 'theme-switch',
103 | () => {
104 | // Look theme-switcher.js
105 | console.log('Switch theme');
106 | }
107 | );
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/assets/webcons/bitbucket.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
29 |
31 |
52 |
57 |
58 |
--------------------------------------------------------------------------------
/assets/webcons/messenger.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
70 |
72 |
74 |
76 |
78 |
80 |
82 |
84 |
86 |
88 |
90 |
92 |
94 |
96 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/assets/webcons/discord.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
57 |
60 |
63 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/assets/webcons/reddit.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
56 |
60 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/assets/webcons/spotify.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
56 |
62 |
65 |
68 |
71 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/assets/theme-buttons/dark-mode.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
40 |
42 |
43 |
45 | image/svg+xml
46 |
48 |
49 |
50 |
51 |
52 |
57 |
60 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/assets/webcons/superuser.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
29 |
31 |
52 |
54 |
57 |
60 |
64 |
65 |
68 |
72 |
73 |
76 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/css/panel-body.css:
--------------------------------------------------------------------------------
1 | #panel-container {
2 | background: transparent;
3 | position: absolute;
4 | z-index: 5;
5 | margin: 0 auto;
6 | bottom: 0;
7 | bottom: 10px;
8 | left: 50%;
9 | transform: translateX(-50%);
10 | }
11 |
12 | #panel-body {
13 | width: auto;
14 | height: auto;
15 | box-sizing: border-box;
16 | padding: 5px;
17 | position: relative;
18 | background: var(--base-bg);
19 | display: inline-flex;
20 | border-radius: 12px;
21 | box-shadow:
22 | 0 1px 1px rgba(0,0,0,0.08),
23 | 0 2px 2px rgba(0,0,0,0.12),
24 | 0 4px 4px rgba(0,0,0,0.16),
25 | 0 8px 8px rgba(0,0,0,0.20);
26 | }
27 |
28 | @media screen and (max-width: 550px) and (min-height: 505px) {
29 | #panel-container {
30 | display: inline-block;
31 | position: absolute;
32 | width: auto;
33 | height: auto;
34 | top: 50%;
35 | bottom: unset;
36 | left: 10px;
37 | transform: translateY(-50%);
38 | }
39 |
40 | #panel-body {
41 | display: inline-block;
42 | position: relative;
43 | width: auto;
44 | height: auto;
45 | bottom: unset;
46 | transform: scale(0.9);
47 | transition: transform var(--transition-speed);
48 | }
49 | }
50 |
51 | @media screen and (max-width: 550px) and (max-height: 504px) {
52 | #panel-container {
53 | display: inline-block;
54 | position: absolute;
55 | width: auto;
56 | height: auto;
57 | top: 50%;
58 | bottom: unset;
59 | left: 10px;
60 | transform: translateY(-50%);
61 | }
62 |
63 | #panel-body {
64 | display: inline-block;
65 | position: relative;
66 | width: auto;
67 | height: auto;
68 | bottom: unset;
69 | transform: scale(0.85);
70 | transition: transform var(--transition-speed);
71 | }
72 | }
73 |
74 | @media screen and (max-width: 550px) and (max-height: 475px) {
75 | #panel-container {
76 | display: inline-block;
77 | position: absolute;
78 | width: auto;
79 | height: auto;
80 | top: 50%;
81 | bottom: unset;
82 | left: 10px;
83 | transform: translateY(-50%);
84 | }
85 |
86 | #panel-body {
87 | display: inline-block;
88 | position: relative;
89 | width: auto;
90 | height: auto;
91 | bottom: unset;
92 | transform: scale(0.8);
93 | transition: transform var(--transition-speed);
94 | }
95 | }
96 |
97 | @media screen and (max-width: 550px) and (max-height: 450px) {
98 | #panel-container {
99 | display: inline-block;
100 | position: absolute;1
101 | width: auto;
102 | height: auto;
103 | top: 50%;
104 | bottom: unset;
105 | left: 10px;
106 | transform: translateY(-50%);
107 | }
108 |
109 | #panel-body {
110 | display: inline-block;
111 | position: relative;
112 | width: auto;
113 | height: auto;
114 | bottom: unset;
115 | transform: scale(0.75);
116 | transition: transform var(--transition-speed);
117 | }
118 | }
119 |
120 | @media screen and (max-height: 420px) {
121 | #panel-container {
122 | display: inline-block;
123 | position: absolute;1
124 | width: auto;
125 | height: auto;
126 | top: 50%;
127 | bottom: unset;
128 | left: 10px;
129 | transform: translateY(-50%);
130 | }
131 |
132 | #panel-body {
133 | display: inline-block;
134 | position: relative;
135 | width: auto;
136 | height: auto;
137 | bottom: unset;
138 | transform: scale(0);
139 | transition: transform var(--transition-speed);
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/assets/webcons/google.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
56 |
60 |
64 |
68 |
72 |
76 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/assets/search-engines/google.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
56 |
60 |
64 |
68 |
72 |
76 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/assets/webcons/soundcloud.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
44 |
47 |
51 |
55 |
59 |
63 |
67 |
68 |
70 |
71 |
73 |
74 |
76 |
77 |
79 |
80 |
82 |
83 |
85 |
86 |
88 |
89 |
91 |
92 |
94 |
95 |
97 |
98 |
100 |
101 |
103 |
104 |
106 |
107 |
109 |
110 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/assets/webcons/4chan.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
57 |
58 |
--------------------------------------------------------------------------------
/assets/webcons/twitch.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
44 |
47 |
51 |
58 |
65 |
66 |
69 |
70 |
73 |
74 |
77 |
78 |
81 |
82 |
85 |
86 |
89 |
90 |
93 |
94 |
97 |
98 |
101 |
102 |
105 |
106 |
109 |
110 |
113 |
114 |
117 |
118 |
121 |
122 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/assets/webcons/gdrive.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
84 |
86 |
90 |
91 |
93 |
97 |
98 |
100 |
104 |
105 |
107 |
111 |
112 |
114 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/assets/webcons/instagram.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
61 |
65 |
69 |
73 |
74 |
77 |
82 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/assets/buttons/theme-switch.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
56 |
60 |
64 |
66 |
68 |
72 |
76 |
80 |
84 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/assets/webcons/gmail.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
56 |
63 |
68 |
72 |
75 |
78 |
81 |
84 |
87 |
90 |
93 |
96 |
99 |
102 |
105 |
108 |
111 |
114 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/assets/webcons/yahoo.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
44 |
47 |
51 |
55 |
56 |
59 |
60 |
63 |
64 |
67 |
68 |
71 |
72 |
75 |
76 |
79 |
80 |
83 |
84 |
87 |
88 |
91 |
92 |
95 |
96 |
99 |
100 |
103 |
104 |
107 |
108 |
111 |
112 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/assets/search-engines/yahoo.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
44 |
47 |
51 |
55 |
56 |
59 |
60 |
63 |
64 |
67 |
68 |
71 |
72 |
75 |
76 |
79 |
80 |
83 |
84 |
87 |
88 |
91 |
92 |
95 |
96 |
99 |
100 |
103 |
104 |
107 |
108 |
111 |
112 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/assets/webcons/commons.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 | Wikimedia Commons Logo
27 |
28 |
29 |
30 |
51 | Wikimedia Commons Logo
53 |
55 |
57 |
62 |
63 |
64 |
67 |
73 |
76 |
79 |
82 |
85 |
86 |
88 |
96 |
104 |
112 |
113 |
121 |
128 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/assets/webcons/jsfiddle.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
29 |
31 |
52 |
57 |
58 |
--------------------------------------------------------------------------------
/assets/buttons/search-engine.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
56 |
62 |
66 |
70 |
74 |
78 |
82 |
86 |
90 |
93 |
96 |
99 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/assets/webcons/wikipedia.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
58 |
--------------------------------------------------------------------------------
/assets/webcons/interneting-is-hard.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 | interneting-is-hard-logo
26 |
27 |
28 |
29 |
50 |
51 | interneting-is-hard-logo
53 | Created with Sketch.
55 |
57 |
64 |
66 |
69 |
73 |
77 |
81 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/assets/buttons/alphabetical.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
44 |
47 |
49 |
52 |
55 |
58 |
59 |
60 |
63 |
64 |
67 |
68 |
71 |
72 |
75 |
76 |
79 |
80 |
83 |
84 |
87 |
88 |
91 |
92 |
95 |
96 |
99 |
100 |
103 |
104 |
107 |
108 |
111 |
112 |
115 |
116 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/assets/webcons/ecosia.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
29 |
31 |
52 |
55 |
59 |
63 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/assets/search-engines/ecosia.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
29 |
31 |
52 |
55 |
59 |
63 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/assets/webcons/ebay.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
85 |
89 |
93 |
95 |
97 |
99 |
101 |
103 |
105 |
107 |
109 |
111 |
113 |
115 |
117 |
119 |
121 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/assets/webcons/amazon.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
29 |
31 |
52 |
57 |
62 |
66 |
71 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/js/suggestions.js:
--------------------------------------------------------------------------------
1 | class AutoSuggestion {
2 | constructor() {
3 | this._searchBox = document.querySelector('#search-box');
4 | this._suggestionsUL = document.querySelector('#suggestions');
5 | this._suggestionsContainer = document.querySelector('#suggestions-container');
6 | this._searchBoxValue = null;
7 | }
8 |
9 | hideSuggestions() {
10 | this._suggestionsContainer.classList.remove('suggestion-show');
11 | this._suggestionsUL.querySelectorAll('*').forEach(child => child.remove());
12 | }
13 |
14 | _showSuggestions() {
15 | this._suggestionsContainer.classList.add('suggestion-show');
16 | }
17 |
18 | _createButtonEvents(button) {
19 |
20 | const suggestionFocus = () => {
21 | if (!this._searchBoxValue) this._searchBoxValue = this._searchBox.value;
22 | const suggestionButtons = Array.prototype.slice.call(document.querySelectorAll('button'));
23 | const suggestionIndex = suggestionButtons.indexOf(document.activeElement) % suggestionButtons.length;
24 | const suggestionButton = suggestionButtons[parseInt(suggestionIndex, 10)];
25 | this._searchBox.value = suggestionButton.innerText;
26 | }
27 |
28 | const nextSuggestion = () => {
29 | if (!this._searchBoxValue) this._searchBoxValue = this._searchBox.value;
30 | const suggestionButtons = Array.prototype.slice.call(document.querySelectorAll('button'));
31 | const suggestionIndex = (suggestionButtons.indexOf(document.activeElement) + 1) % suggestionButtons.length;
32 | const suggestionButton = suggestionButtons[parseInt(suggestionIndex, 10)];
33 | this._searchBox.value = suggestionButton.innerText;
34 | suggestionButton.focus();
35 | }
36 |
37 | const previousSuggestion = () => {
38 | if (!this._searchBoxValue) this._searchBoxValue = this._searchBox.value;
39 | const suggestionButtons = Array.prototype.slice.call(document.querySelectorAll('button'));
40 | let suggestionIndex = (suggestionButtons.indexOf(document.activeElement) - 1) % suggestionButtons.length;
41 |
42 | if (suggestionIndex < 0) {
43 | suggestionIndex = suggestionButtons.length - 1;
44 | }
45 |
46 | const suggestionButton = suggestionButtons[parseInt(suggestionIndex, 10)];
47 | this._searchBox.value = suggestionButton.innerText;
48 | suggestionButton.focus();
49 | }
50 |
51 | const focusSearchBox = () => {
52 | if (this._searchBoxValue) {
53 | this._searchBox.value = this._searchBoxValue
54 | this._searchBoxValue = null;
55 | }
56 | this._searchBox.focus();
57 | }
58 |
59 | const querySend = () => {
60 | this._searchBox.value = button.innerText;
61 | searchQuerySend.sendQuery();
62 | }
63 |
64 | const keyUpEvents = {
65 | 'Enter': function() {
66 | querySend();
67 | },
68 | 'Backspace': function() {
69 | focusSearchBox();
70 | },
71 | 'Tab': function() {
72 | suggestionFocus();
73 | },
74 | 'ArrowDown': function() {
75 | nextSuggestion();
76 | },
77 | 'ArrowRight': function() {
78 | nextSuggestion();
79 | },
80 | 'ArrowUp': function() {
81 | previousSuggestion();
82 | },
83 | 'ArrowLeft': function() {
84 | previousSuggestion();
85 | }
86 | };
87 |
88 | button.addEventListener(
89 | 'keyup',
90 | e => {
91 | e.preventDefault();
92 | const callback = keyUpEvents[String(e.key)];
93 | if (typeof callback === 'function') {
94 | callback();
95 | }
96 | }
97 | );
98 |
99 | button.addEventListener(
100 | 'click',
101 | e => {
102 | querySend();
103 | }
104 | );
105 | }
106 |
107 | _parseSuggestionsObject(phrase) {
108 |
109 | // Filter/parse the object
110 | const suggestion = phrase.map(i => i.phrase)
111 | .filter(s => !(s.toLowerCase() === String(this._searchBox.value).toLowerCase()))
112 | .slice(0, 4);
113 |
114 | // Empty UL
115 | this._suggestionsUL.querySelectorAll('*').forEach(child => child.remove());
116 |
117 | // Generate list elements
118 | for (let phrases of suggestion) {
119 |
120 | // Create HTML elements
121 | const li = document.createElement('li');
122 | li.className = 'suggestion';
123 |
124 | const button = document.createElement('button');
125 | button.type = 'button';
126 | button.className = 'suggestion-button';
127 | button.innerText = phrases;
128 |
129 | // Create input events
130 | this._createButtonEvents(button);
131 |
132 | // Appent to ul
133 | li.appendChild(button);
134 | this._suggestionsUL.appendChild(li);
135 | }
136 |
137 | // Show suggestions if searchbox > 1
138 | if (this._searchBox.value.length > 1) {
139 | this._showSuggestions();
140 | }
141 | }
142 |
143 | fetchSuggestions() {
144 | const endpoint = 'https://duckduckgo.com/ac/';
145 | const callback = 'autocompleteCallback';
146 | const searchQuery = String(this._searchBox.value);
147 | window[String(callback)] = res => {
148 | // Pass the suggestion object to process it
149 | this._parseSuggestionsObject(res);
150 | };
151 |
152 | // Fetch from duckduckgo
153 | const script = document.createElement('script');
154 | script.type = 'text/javascript';
155 | script.src = `${endpoint}?callback=${callback}&q=${searchQuery}`;
156 | document.querySelector('head').appendChild(script);
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Home
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
63 |
68 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 | Oof! Does your browser support JavaScript? If you think so, enable it!
130 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/assets/webcons/calendar.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
53 |
56 |
58 |
60 |
64 |
65 |
69 |
71 |
73 |
75 |
79 |
80 |
81 |
85 |
87 |
93 |
94 |
96 |
102 |
103 |
105 |
109 |
110 |
114 |
116 |
120 |
121 |
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/css/web-menu.css:
--------------------------------------------------------------------------------
1 | #web-menu {
2 | position: fixed;
3 | top: 0;
4 | left: 0;
5 | width: 100%;
6 | height: 100%;
7 | margin: 0;
8 | background: var(--base-bg);
9 | overflow: hidden;
10 | user-select: none;
11 | box-sizing: border-box;
12 | z-index: -1;
13 | padding: 40px 12vw 6vh 12vw;
14 | opacity: 0;
15 | transition:
16 | opacity var(--transition-speed),
17 | z-index var(--transition-speed);
18 | }
19 |
20 | #web-menu.web-menu-show {
21 | opacity: 1;
22 | z-index: 3;
23 | }
24 |
25 | #web-menu-body {
26 | background: transparent;
27 | max-height: 100vh;
28 | overflow: hidden;
29 | }
30 |
31 | #web-menu-searchbox-container {
32 | position: relative;
33 | display: flex;
34 | flex-flow: column wrap;
35 | align-items: center;
36 | margin-bottom: 20px;
37 | }
38 |
39 | #web-menu-list-container {
40 | position: relative;
41 | display: flex;
42 | justify-content: center;
43 | max-height: 70vh;
44 | overflow-y: auto;
45 | scrollbar-color: var(--base-secondary-bg) var(--base-bg);
46 | scrollbar-width: 10px;
47 | }
48 |
49 | #web-menu-list-container.web-menu-category-mode {
50 | display: block;
51 | }
52 |
53 | #web-menu-searchbox {
54 | background: var(--base-secondary-bg);
55 | color: var(--font-color);
56 | font-size: 12pt;
57 | font-weight: 700;
58 | text-align: center;
59 | padding: 2px;
60 | border: none;
61 | border-radius: 6px;
62 | width: 225px;
63 | height: 36px;
64 | }
65 |
66 | #web-menu-searchbox::placeholder {
67 | font-size: 11pt;
68 | font-weight: 700;
69 | }
70 |
71 | /* Unordered list */
72 | #web-menu-list {
73 | list-style-type: none;
74 | padding: 0;
75 | margin: 0 auto;
76 | flex-grow: 1;
77 | text-align: justify;
78 | background: transparent;
79 | }
80 |
81 | #web-menu-list.web-menu-list-hide {
82 | display: none;
83 | }
84 |
85 | /* List */
86 | #web-menu-list li {
87 | display: inline-block;
88 | }
89 |
90 | /* Selected list */
91 | #web-menu-list li.selected {
92 | background: #ff00ff;
93 | border-radius: 6px;
94 | }
95 |
96 | /* Element inside the list item */
97 | .web-item {
98 | background: var(--base-secondary-bg);
99 | width: 128px;
100 | height: 128px;
101 | margin: 5px;
102 | cursor: pointer;
103 | border-radius: 12px;
104 | }
105 |
106 | .web-item:hover {
107 | box-shadow: 0 0 0 1px var(--hover-bg) inset;
108 | }
109 |
110 | .web-item:active {
111 | background: var(--active-bg);
112 | box-shadow: 0 0 0 1px var(--active-bg) inset;
113 | }
114 |
115 | .web-item-focus {
116 | background: var(--active-bg);
117 | }
118 |
119 | .web-item-container {
120 | margin: 0 auto;
121 | position: relative;
122 | top: 50%;
123 | transform: translateY(-50%);
124 | display: flex;
125 | flex-direction: row;
126 | justify-content: center;
127 | padding: 5px;
128 | }
129 |
130 | .web-item-icon-container {
131 | position: relative;
132 | display: flex;
133 | flex-flow: column wrap;
134 | align-items: center;
135 | }
136 |
137 | .web-item-icon {
138 | height: 64px;
139 | width: 64px;
140 | margin-bottom: 0;
141 | background-size: cover;
142 | }
143 |
144 | .web-item-name {
145 | text-align: center;
146 | font-size: 11pt;
147 | font-weight: 400;
148 | word-wrap: break-word;
149 | color: var(--font-color);
150 | }
151 |
152 | .web-menu-link {
153 | display: block;
154 | text-decoration: none;
155 | outline: 0;
156 | border: none;
157 | outline-style: none;
158 | user-select: none;
159 | }
160 |
161 | #web-menu-categorized {
162 | list-style-type: none;
163 | padding: 0;
164 | margin: 0 auto;
165 | }
166 |
167 | #web-menu-categorized.web-menu-categorized-hide {
168 | display: none;
169 | }
170 |
171 | .category-body {
172 | margin: 10px 5px;
173 | }
174 |
175 | .category-name {
176 | font-weight: 700;
177 | color: var(--font-color);
178 | margin: 0 5px;
179 | }
180 |
181 | .category-list {
182 | list-style-type: none;
183 | padding: 0;
184 | margin: 0 auto;
185 | flex-grow: 1;
186 | text-align: justify;
187 | background: transparent;
188 | overflow: hidden;
189 | }
190 |
191 | .category-list li {
192 | display: inline-block;
193 | }
194 |
195 | #web-menu-mode-switcher-container {
196 | position: absolute;
197 | top: 0;
198 | right: 0;
199 | width: auto;
200 | height: auto;
201 | margin: 42px 42px;
202 | border-radius: 50%;
203 | }
204 |
205 | #web-menu-mode-switcher {
206 | background: var(--base-secondary-bg);
207 | border: none;
208 | outline: none;
209 | border-radius: 50%;
210 | width: 35px;
211 | height: 35px;
212 | padding: 10px;
213 | cursor: pointer;
214 | }
215 |
216 | #web-menu-mode-switcher:hover {
217 | background: var(--button-hover);
218 | }
219 |
220 | #web-menu-mode-switcher:active {
221 | background: var(--button-active);
222 | }
223 |
224 | #web-menu-mode-switcher:selected {
225 | background: var(--button-active);
226 | }
227 |
228 | #web-menu-mode-switcher-img {
229 | background-image: url('../assets/buttons/alphabetical.svg');
230 | background-size: cover;
231 | filter: var(--image-invert);
232 | width: 100%;
233 | height: 100%;
234 | }
235 |
236 | #web-menu-mode-switcher-img.category-mode {
237 | background-image: url('../assets/buttons/categories.svg');
238 | background-size: cover;
239 | filter: var(--image-invert);
240 | width: 100%;
241 | height: 100%;
242 | }
243 |
244 | @media screen and (max-width: 470px) {
245 | #web-menu-searchbox {
246 | width: 50vw;
247 | }
248 |
249 | #web-menu {
250 | padding: 6vh 18vw 0 18vw;
251 | }
252 |
253 | #web-menu-list {
254 | flex-grow: 1;
255 | }
256 |
257 | #web-menu-list li {
258 | display: inline;
259 | }
260 |
261 | .web-item {
262 | width: auto;
263 | margin: 5px 1.2rem;
264 | }
265 |
266 | .web-item:hover {
267 | transform: scale(1);
268 | }
269 |
270 | .web-item-focus {
271 | transform: scale(1);
272 | }
273 |
274 | #web-menu-list-container {
275 | scrollbar-width: none;
276 | -ms-overflow-style: none;
277 | }
278 |
279 | #web-menu-list-container::-webkit-scrollbar {
280 | display: none;
281 | }
282 |
283 | #web-menu-mode-switcher-container {
284 | top: unset;
285 | right: 0;
286 | bottom: 0;
287 | }
288 |
289 | .category-name {
290 | text-align: center;
291 | }
292 |
293 | .category-list {
294 | flex-grow: 1;
295 | }
296 |
297 | .category-list li {
298 | display: inline;
299 | }
300 | }
301 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## squareup
2 |
3 | ### A sleek and modern startpage
4 |
5 | ## [Live Demo](https://manilarome.github.io/squareup/)
6 |
7 |
8 |
9 |
10 |
11 | squareup startpage
12 |
13 |
14 |
15 | ## Features
16 |
17 | + Responsive UI
18 | + Web Search Suggestions
19 | + Mobile Support
20 | + Theme Switcher
21 | + Keyboard navigation
22 | + Search Engine Selection
23 | + Animated Background
24 | + Web Menu with Fuzzy Search
25 | + Vanilla Javascript!
26 | + And many bugs!
27 |
28 | ## Usage
29 |
30 | + Clone this repo.
31 | + Set it as your default homepage.
32 |
33 | ## Keybindings
34 |
35 | + Escape - toggles web menu
36 | + Control + Space - switches search engine
37 | + Alt + Space - switches color scheme
38 |
39 | ## Quick search
40 |
41 | + `r/` + `subreddit name` will open the subreddit if valid or existing.
42 | - `r/unixporn`
43 | - `r/startpages`
44 |
45 | + `w/` + `search query` to search on wikipedia.
46 | - `w/linux`
47 | - `w/Javascript`
48 |
49 | + `u/` + `search query` to search for an image/photo on unsplash.
50 | - `u/nature`
51 | - `u/technology`
52 |
53 | + `a/` + `search query` to search a product on amazon.
54 | - `a/intel celeron`
55 | - `a/windows 10 source code`
56 |
57 | + `e/` + `search query` to search a product on ebay.
58 | - `e/pentium 4`
59 | - `e/uranium core`
60 |
61 | + `y/` + `search query` to search a video on youtube.
62 | - `y/how to build a nuclear reactor`
63 | - `y/strange alien sightings in oregon`
64 |
65 | + `n/` + `comic id` to search a "comic" on a certain "comic" website.
66 | - `n/177013`
67 |
68 | + `g/` + `search query` to search a for a repo/user on github.
69 | - `g/manilarome`
70 | - `g/squareup`
71 | - `g/manilarome/squareup`
72 |
73 | ## URL Redirects
74 |
75 | Searching a valid URL will redirect you to the said URL. Note that a protocol, `https://` for example, is required.
76 |
77 | + `https://haveibeenpwned.com/` query is valid, so you will be redirected to [https://haveibeenpwned.com/](https://haveibeenpwned.com/).
78 | + `google.com` is not a valid URL, so it will search it on your default search engine.
79 | + `www.duckduckgo.com` is also invalid because it doesn't have a protocol.
80 |
81 | ## URL Query Parameters
82 |
83 | You can also pass a query by using the `q` parameter. The default search engine will be used.
84 |
85 | + `manilarome.github.io/squareup?q=how to build a nuclear reactor at home`
86 | + `127.0.0.1?q=how to download more RAM in google play store`
87 | + `file:///PATH/TO/squareup/index.html?q=how to restore system32`
88 |
89 | ## Settings and Customization
90 |
91 | ### Customizing color scheme
92 |
93 | Change the color scheme by just clicking a button!
94 |
95 | + `Dark` - Dark colorscheme. Good for the night.
96 | + `Light` - Bright colorscheme. Good for killing the eyes.
97 | + `Auto` - Load a colorscheme based on time. Edit light/dark mode hours on `js/config.js`
98 |
99 | #### Customizing panel buttons
100 |
101 | To add more web shortcuts/buttons on the dock, you have to edit the `panelSites` array in `js/config.js`. Make sure to put an icon with `svg` format for the shortcut in `assets/webcons/` folder.
102 |
103 | ```js
104 | // Example
105 | const panelSites = [
106 | {
107 | site: 'Reddit',
108 | icon: 'reddit',
109 | url: 'https://reddit.com/'
110 | },
111 | ...
112 | ]
113 | ```
114 |
115 | #### Customizing web menu
116 |
117 | Add more items or web shortcuts in the web menu by editing the `webSites` array in `js/config.js`. Make sure to put an icon with `svg` format for the shortcut in `assets/webcons/` folder.
118 |
119 | ```js
120 | // Example
121 | const webSites = [
122 | {
123 | site: 'Reddit',
124 | icon: 'reddit',
125 | url: 'https://reddit.com/',
126 | category: 'social'
127 | },
128 | ...
129 | ]
130 | ```
131 |
132 | #### Customizing quick search
133 |
134 | Add more quick search shortcuts by editing the `quickSearchData` object in `js/config.js`. Make sure to follow the format below:
135 |
136 | ```js
137 | // Example
138 | const quickSearchData = {
139 | 'r/': {
140 | urlPrefix: 'https://reddit.com/r/'
141 | },
142 | ...
143 | }
144 | ```
145 |
146 | #### Switch default search engine
147 |
148 | Startpage is the default search engine, if you want to change it, just click the switcher button on the panel.
149 |
150 | Available search engines:
151 |
152 | + Startpage
153 | + Qwant
154 | + Ecosia
155 | + Duckduckgo
156 | + Yahoo
157 | + Google
158 | + Bing
159 |
160 | #### Customizing available search engines
161 |
162 | Add more search engine by editing the `searchEngines` object in `js/config.js`. Make sure to follow the format below:
163 |
164 | ```js
165 | // Example
166 | const searchEngines = {
167 | 'duckduckgo': {
168 | name: 'Duckduckgo',
169 | prefix: 'https://duckduckgo.com/?q='
170 | },
171 | ...
172 | }
173 | ```
174 |
175 | #### Changing clock mode
176 |
177 | There are two clock modes available - `24-hour` and `12-hour`. Switch between clock modes by just clicking on the clock. Simple.
178 |
179 | ### Important Note
180 |
181 | + Make sure that javascript is enabled in your browser!
182 | + Make sure to whitelist or disable `NoScript` and `Dark Mode Reader` extensions on this homepage.
183 | + If you are experiencing slowdowns, you can disable the animations in `css/animated-background.css`.
184 | + Tested only on Firefox and Google Chrome.
185 |
186 | ### TODOs
187 |
188 | Squareup will sit between minimal and bloated. The items in this TODO list are the only one I'm planning to implement in this homepage.
189 |
190 | - [x] Search engine switching
191 | - [x] Autosuggestion
192 | - [x] Categorized Web Menu
193 |
194 |
195 | ### Issues?
196 |
197 | Feel free to open one!
198 |
199 | ### PR?
200 |
201 | That would be great!
202 |
203 | ### Other works
204 |
205 | Check my other works related to this startpage.
206 |
207 | + [blurredfox](https://github.com/manilarome/blurredfox/) - a gorgeous and modern firefox CSS theme.
208 | + [the-glorious-startpage](https://github.com/manilarome/the-glorious-startpage) - my very first startpage. Kinda bloaty and code is quite messy but still kinda works and looks gorgeous.
209 | + [the-glorious-dotfiles](https://github.com/manilarome/the-glorious-dotfiles) - a stash of my configurations.
210 |
--------------------------------------------------------------------------------