├── 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 | 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 |
48 |
49 |
50 | 51 |
52 |
    53 | 54 | 59 |
60 |
61 |
62 |
63 |
64 |
65 | 66 |
67 |
68 |
69 |
70 |
71 | 72 |
73 | 74 |
75 |
    76 | 85 |
86 |
    87 | 104 |
105 |
106 |
107 |
108 | 111 |
112 |
113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 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 | squareup 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 | --------------------------------------------------------------------------------