├── src ├── public │ ├── .nojekyll │ ├── preview.png │ ├── images │ │ ├── aira-1.webp │ │ ├── aira-2.webp │ │ ├── jiji-1.webp │ │ ├── momo-1.webp │ │ ├── momo-2.webp │ │ ├── seiko-1.webp │ │ ├── seiko-2.webp │ │ ├── granny-1.webp │ │ ├── granny-2.webp │ │ ├── okarun-1.webp │ │ ├── okarun-2.webp │ │ └── okarun-3.webp │ ├── fonts │ │ ├── quicksand-bold.ttf │ │ └── quicksand-medium.ttf │ └── favicon.svg ├── styles │ ├── index.css │ ├── font.css │ ├── global.css │ ├── theme.css │ └── main.css ├── scripts │ ├── bookmarks.js │ ├── index.js │ └── variants.js └── index.html ├── preview.png ├── .gitignore ├── vite.config.js ├── package.json ├── LICENSE ├── README.md └── .github └── workflows └── deploy.yml /src/public/.nojekyll: -------------------------------------------------------------------------------- 1 | # Note: Don't delete, needed for github pages to work correctly -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/preview.png -------------------------------------------------------------------------------- /src/public/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/preview.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | *.log 3 | node_modules 4 | dist 5 | build 6 | .vscode/* 7 | .idea/* 8 | !.vscode/extensions.json 9 | -------------------------------------------------------------------------------- /src/public/images/aira-1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/images/aira-1.webp -------------------------------------------------------------------------------- /src/public/images/aira-2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/images/aira-2.webp -------------------------------------------------------------------------------- /src/public/images/jiji-1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/images/jiji-1.webp -------------------------------------------------------------------------------- /src/public/images/momo-1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/images/momo-1.webp -------------------------------------------------------------------------------- /src/public/images/momo-2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/images/momo-2.webp -------------------------------------------------------------------------------- /src/public/images/seiko-1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/images/seiko-1.webp -------------------------------------------------------------------------------- /src/public/images/seiko-2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/images/seiko-2.webp -------------------------------------------------------------------------------- /src/styles/index.css: -------------------------------------------------------------------------------- 1 | @import "./theme.css"; 2 | @import "./global.css"; 3 | @import "./font.css"; 4 | @import "./main.css"; 5 | -------------------------------------------------------------------------------- /src/public/images/granny-1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/images/granny-1.webp -------------------------------------------------------------------------------- /src/public/images/granny-2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/images/granny-2.webp -------------------------------------------------------------------------------- /src/public/images/okarun-1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/images/okarun-1.webp -------------------------------------------------------------------------------- /src/public/images/okarun-2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/images/okarun-2.webp -------------------------------------------------------------------------------- /src/public/images/okarun-3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/images/okarun-3.webp -------------------------------------------------------------------------------- /src/public/fonts/quicksand-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/fonts/quicksand-bold.ttf -------------------------------------------------------------------------------- /src/public/fonts/quicksand-medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrettyCoffee/dandadan-startpage/HEAD/src/public/fonts/quicksand-medium.ttf -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite" 2 | 3 | export default defineConfig({ 4 | base: "/dandadan-startpage/", 5 | root: "./src" 6 | }) -------------------------------------------------------------------------------- /src/public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/font.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Quicksand'; 3 | font-style: normal; 4 | font-weight: 500; 5 | src: url("/fonts/quicksand-medium.ttf") format("truetype"); 6 | } 7 | 8 | @font-face { 9 | font-family: 'Quicksand'; 10 | font-style: normal; 11 | font-weight: 700; 12 | font-display: swap; 13 | src: url(/fonts/quicksand-bold.ttf) format('truetype'); 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dandadanstartpage", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview", 10 | "lint": "eslint ./src", 11 | "lint:fix": "npm run lint -- --fix" 12 | }, 13 | "devDependencies": { 14 | "@pretty-cozy/eslint-config": "^0.4.0", 15 | "vite": "^5.4.11" 16 | }, 17 | "eslintConfig": { 18 | "extends": [ 19 | "@pretty-cozy/eslint-config/base" 20 | ], 21 | "env": { 22 | "browser": true 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/scripts/bookmarks.js: -------------------------------------------------------------------------------- 1 | const createLink = (text, url) => { 2 | const link = document.createElement("a") 3 | 4 | link.innerText = text 5 | link.href = url 6 | link.setAttribute("data-text", text) 7 | return link 8 | } 9 | 10 | const nav = document.getElementById("bookmarks") 11 | 12 | export function injectBookmarks(bookmarks) { 13 | // Remove all existing content before adding new content 14 | nav.innerHTML = "" 15 | 16 | bookmarks.map(({ label, items }) => { 17 | const list = document.createElement("ul") 18 | 19 | const groupLabel = document.createElement("label") 20 | groupLabel.innerText = label 21 | list.append(groupLabel) 22 | 23 | Object.entries(items).forEach(([name, url]) => { 24 | const item = document.createElement("li") 25 | const link = createLink(name, url) 26 | item.appendChild(link) 27 | list.appendChild(item) 28 | }) 29 | nav.appendChild(list) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/styles/global.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::before, 3 | *::after { 4 | box-sizing: border-box; 5 | } 6 | 7 | * { 8 | margin: 0; 9 | } 10 | 11 | input, 12 | button, 13 | textarea, 14 | select { 15 | font: inherit; 16 | color: currentColor; 17 | outline: none; 18 | } 19 | 20 | ul { 21 | list-style: none; 22 | padding: 0; 23 | } 24 | 25 | html { 26 | height: 100%; 27 | } 28 | body { 29 | height: max-content; 30 | min-height: 100%; 31 | } 32 | 33 | .visually-hidden { 34 | display: inline-block; 35 | position: absolute; 36 | overflow: hidden; 37 | clip: rect(0 0 0 0); 38 | height: 1px; 39 | width: 1px; 40 | margin: -1px; 41 | padding: 0px; 42 | border: 0px; 43 | } 44 | 45 | /** 46 | * Scrollbar 47 | **/ 48 | 49 | :root { 50 | --scrollbar-track: var(--background); 51 | --scrollbar-thumb: var(--text); 52 | } 53 | 54 | /* web standard */ 55 | * { 56 | scrollbar-width: auto; 57 | scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track); 58 | } 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 PrettyCoffee 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DanDaDan Startpage 2 | 3 | ![startpage preview](./preview.png) 4 | 5 | A simple startpage, iterating over multiple images from the DanDaDan opening. 6 | 7 | ## How to use 8 | 9 | To use it yourself, fork the repo and set it up to automatically deploy the startpage: 10 | 1. In your repo, open the `Settings` and in there the `Pages` page 11 | 2. Click on the `Source` select and choose `Github Actions` 12 | 3. Switch to the `Actions` tab, click on `Deploy to Pages` and then `Run workflow` 13 | 4. When reloading the page, a deployment job should be pending now. 14 | After completion, your page is deployed on `https://.github.io/dandadan-startpage/` 15 | 5. See [Customizability](#customizability) on how to adjust the content 16 | 17 | Then you need to set it up in your browser. 18 | The easiest is to pick a `New tab override` browser extension in your browsers extension store and insert your page url. 19 | 20 | ## Customizability 21 | 22 | Content can be adjusted in `src/scripts/index.js`: 23 | - Adjust bookmarks by updating the `bookmarks` variable 24 | - You can "pin" an image variant by passing its name in the `updateVariant` function 25 | - e.g.: `updateVariant("momo-1")` 26 | - All variant names can be found in `src/scripts/variants.js` 27 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to Pages 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | 7 | # Allows to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 11 | permissions: 12 | contents: read 13 | pages: write 14 | id-token: write 15 | 16 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 17 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 18 | concurrency: 19 | group: "pages" 20 | cancel-in-progress: false 21 | 22 | jobs: 23 | build: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | 29 | - name: Setup Node 30 | uses: actions/setup-node@v4 31 | with: 32 | node-version: "20" 33 | cache: "npm" 34 | 35 | - name: Setup Pages 36 | uses: actions/configure-pages@v3 37 | 38 | - name: Install dependencies 39 | run: npm ci 40 | 41 | - name: Build project 42 | run: npm run build 43 | 44 | - name: Upload artifact 45 | uses: actions/upload-pages-artifact@v3 46 | with: 47 | path: ./src/dist 48 | 49 | deploy: 50 | name: github-pages 51 | runs-on: ubuntu-latest 52 | needs: build 53 | steps: 54 | - name: Deploy to GitHub Pages 55 | id: deployment 56 | uses: actions/deploy-pages@v4 57 | -------------------------------------------------------------------------------- /src/scripts/index.js: -------------------------------------------------------------------------------- 1 | import { injectBookmarks } from "./bookmarks.js" 2 | import { updateVariant } from "./variants.js" 3 | 4 | const bookmarks = [ 5 | { 6 | label: "reddit", 7 | items: { 8 | "r/startpages": "https://www.reddit.com/r/startpages", 9 | "r/unixporn": "https://www.reddit.com/r/unixporn", 10 | "r/webdev": "https://www.reddit.com/r/webdev", 11 | "r/coolgithubprojects ": "https://www.reddit.com/r/coolgithubprojects ", 12 | }, 13 | }, 14 | { 15 | label: "tools", 16 | items: { 17 | "img > compress": "https://compressimage.io/", 18 | "img > upscale": "https://bigjpg.com/", 19 | "css > shadows": "https://www.joshwcomeau.com/shadow-palette/", 20 | "js > bundlephobia": "https://bundlephobia.com/", 21 | }, 22 | }, 23 | { 24 | label: "libs", 25 | items: { 26 | tailwind: "https://tailwindcss.com/docs/customizing-colors", 27 | shadcn: "https://ui.shadcn.com/docs/components", 28 | }, 29 | }, 30 | { 31 | label: "ressources", 32 | items: { 33 | "react > bulletproof": "https://github.com/alan2207/bulletproof-react", 34 | "js > patterns": "https://www.patterns.dev/#patterns", 35 | }, 36 | }, 37 | ] 38 | 39 | injectBookmarks(bookmarks) 40 | 41 | // Select a random variant 42 | updateVariant() 43 | // Or set a static variant 44 | // updateVariant("momo-1") 45 | 46 | // For debugging 47 | /* 48 | function iterateVariants() { 49 | updateVariant() 50 | setTimeout(iterateVariants, 5000) 51 | } 52 | iterateVariants() 53 | */ 54 | -------------------------------------------------------------------------------- /src/styles/theme.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Tokens 3 | **/ 4 | 5 | :root { 6 | --background: #09090b; 7 | 8 | /* colors are set in JS based on variant */ 9 | --primary: var(--background); 10 | --secondary: var(--background); 11 | --shade-color: var(--background); 12 | 13 | --text: hsl(from var(--primary) h s 90%); 14 | --text-gentle: hsla(from var(--primary) h s 80% / 70%); 15 | } 16 | 17 | :root { 18 | --radius-sm: 0.25rem; 19 | --radius-md: 0.5rem; 20 | --focus-ring: 0.125rem solid var(--secondary); 21 | } 22 | 23 | /** 24 | * Typography 25 | **/ 26 | 27 | :root { 28 | font-family: Quicksand, Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 29 | line-height: 1.5; 30 | font-weight: 500; 31 | 32 | color: var(--text); 33 | background: var(--background); 34 | 35 | font-synthesis: none; 36 | text-rendering: optimizeLegibility; 37 | -webkit-font-smoothing: antialiased; 38 | -moz-osx-font-smoothing: grayscale; 39 | } 40 | 41 | /** 42 | * Elements 43 | **/ 44 | 45 | button { 46 | height: 2.5rem; 47 | min-width: 2.5rem; 48 | border: 0.125rem solid transparent; 49 | 50 | display: inline-flex; 51 | align-items: center; 52 | justify-content: center; 53 | 54 | border-radius: var(--radius-sm); 55 | background: transparent; 56 | cursor: pointer; 57 | 58 | &:hover { 59 | color: var(--background); 60 | background: var(--primary); 61 | } 62 | &:focus-visible { 63 | border-color: var(--secondary); 64 | } 65 | } 66 | 67 | input { 68 | height: 2.5rem; 69 | width: 100%; 70 | min-width: 2.5rem; 71 | border: 0.125rem solid var(--primary); 72 | padding: 0 0.5rem; 73 | 74 | background: transparent; 75 | border-radius: var(--radius-sm); 76 | 77 | &::placeholder { 78 | color: var(--text-gentle); 79 | } 80 | &:focus-visible { 81 | border-color: var(--secondary); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | New Tab 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 | 37 |

こんにちは

38 | 39 |
40 | 41 | 48 |
49 | 50 | 53 |
54 | 55 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/scripts/variants.js: -------------------------------------------------------------------------------- 1 | const variants = { 2 | "aira-1": { 3 | img: "aira-1.webp", 4 | primary: "hsl(41, 93%, 73%)", 5 | secondary: "hsl(324, 49%, 80%)", 6 | shade: "hsl(41, 39%, 26%, 39%)", 7 | }, 8 | "aira-2": { 9 | img: "aira-2.webp", 10 | primary: "hsl(0, 86%, 67%)", 11 | secondary: "hsl(214, 100%, 75%)", 12 | shade: "hsl(360, 43%, 24%, 39%)", 13 | }, 14 | "granny-1": { 15 | img: "granny-1.webp", 16 | primary: "hsl(176, 93%, 58%)", 17 | secondary: "hsl(270, 36%, 70%)", 18 | shade: "hsl(177, 72%, 20%, 39%)", 19 | }, 20 | "granny-2": { 21 | img: "granny-2.webp", 22 | primary: "hsl(176, 93%, 58%)", 23 | secondary: "hsl(6, 93%, 70%)", 24 | shade: "hsl(177, 72%, 20%, 39%)", 25 | }, 26 | "jiji-1": { 27 | img: "jiji-1.webp", 28 | primary: "hsl(235, 59%, 33%)", 29 | secondary: "hsl(5, 78%, 74%)", 30 | shade: "hsl(236, 79%, 9%, 39%)", 31 | }, 32 | "momo-1": { 33 | img: "momo-1.webp", 34 | primary: "hsl(261, 81%, 43%)", 35 | secondary: "hsl(163, 66%, 47%)", 36 | shade: "hsl(261, 98%, 14%, 39%)", 37 | }, 38 | "momo-2": { 39 | img: "momo-2.webp", 40 | primary: "hsl(190, 47%, 44%)", 41 | secondary: "hsl(316, 45%, 67%)", 42 | shade: "hsl(191, 58%, 14%, 39%)", 43 | }, 44 | "okarun-1": { 45 | img: "okarun-1.webp", 46 | primary: "hsl(21, 93%, 52%)", 47 | secondary: "hsl(243, 17%, 70%)", 48 | shade: "hsl(21, 97%, 17%, 39%)", 49 | }, 50 | "okarun-2": { 51 | img: "okarun-2.webp", 52 | primary: "hsl(230, 59%, 40%)", 53 | secondary: "hsl(348, 50%, 73%)", 54 | shade: "hsl(230, 76%, 12%, 39%)", 55 | }, 56 | "okarun-3": { 57 | img: "okarun-3.webp", 58 | primary: "hsl(172, 68%, 67%)", 59 | secondary: "hsl(294, 50%, 75%)", 60 | shade: "hsl(173, 34%, 23%, 39%)", 61 | }, 62 | "seiko-1": { 63 | img: "seiko-1.webp", 64 | primary: "hsl(195, 61%, 26%)", 65 | secondary: "hsl(23, 59%, 65%)", 66 | shade: "hsl(196, 86%, 7%, 39%)", 67 | }, 68 | "seiko-2": { 69 | img: "seiko-2.webp", 70 | primary: "hsl(89, 93%, 76%)", 71 | secondary: "hsl(131, 52%, 70%)", 72 | shade: "hsl(90, 35%, 27%, 39%)", 73 | }, 74 | } 75 | 76 | function getRandomVariant() { 77 | const list = Object.keys(variants) 78 | const min = 0 79 | const max = list.length - 1 80 | 81 | const randomIndex = Math.floor(Math.random() * (max - min + 1)) + min 82 | return list[randomIndex] 83 | } 84 | 85 | const pinAction = document.getElementById("pin-action") 86 | const pinStoreKey = "dandadan-spg/pinned" 87 | 88 | function getPinned() { 89 | return localStorage.getItem(pinStoreKey) 90 | } 91 | 92 | function setPinned(variant) { 93 | const label = pinAction.querySelector("&>span.visually-hidden") 94 | const icon = pinAction.querySelector("&>svg") 95 | 96 | if (!variant) { 97 | localStorage.removeItem(pinStoreKey) 98 | pinAction.ariaChecked = "false" 99 | icon.style.fill = "transparent" 100 | pinAction.title = label.innerText = "Pin variant" 101 | } else { 102 | localStorage.setItem(pinStoreKey, variant) 103 | pinAction.ariaChecked = "true" 104 | icon.style.fill = "currentColor" 105 | pinAction.title = label.innerText = "Unpin variant" 106 | } 107 | } 108 | 109 | if (getPinned()) { 110 | setPinned(getPinned()) 111 | } 112 | 113 | export function updateVariant(variant) { 114 | const variantId = variant ?? getPinned() ?? getRandomVariant() 115 | 116 | pinAction.addEventListener("click", ({ target }) => { 117 | if (target.ariaChecked === "true") { 118 | setPinned(null) 119 | } else { 120 | setPinned(variantId) 121 | } 122 | }) 123 | 124 | const current = variants[variantId] 125 | 126 | const img = document.getElementById("character") 127 | img.src = `./images/${current.img}` 128 | 129 | const root = document.documentElement 130 | root.style.setProperty("--primary", current.primary) 131 | root.style.setProperty("--secondary", current.secondary) 132 | root.style.setProperty("--shade-color", current.shade) 133 | } 134 | -------------------------------------------------------------------------------- /src/styles/main.css: -------------------------------------------------------------------------------- 1 | :root { 2 | background: var(--background) 3 | linear-gradient(120deg, 4 | rgba(from var(--primary) r g b / 75%) 0%, 5 | rgba(from var(--primary) r g b / 75%) 100% 6 | ); 7 | } 8 | 9 | body { 10 | padding: 3.5rem 2rem; 11 | display: flex; 12 | align-items: center; 13 | justify-content: center; 14 | } 15 | 16 | button#pin-action { 17 | position: absolute; 18 | top: 0.5rem; 19 | right: 0.5rem; 20 | opacity: 0.5; 21 | svg { 22 | rotate: 30deg; 23 | } 24 | &:hover { 25 | opacity: 1; 26 | } 27 | } 28 | 29 | body>main { 30 | padding: 1rem 2rem 1rem 1rem; 31 | backdrop-filter: blur(4px); 32 | 33 | display: grid; 34 | column-gap: 2rem; 35 | row-gap: 1rem; 36 | grid-template-rows: auto auto 1fr; 37 | grid-template-areas: 38 | "char-img title" 39 | "char-img nav" 40 | "search nav"; 41 | 42 | background: hsla(from var(--background) h s l / 40%); 43 | border-radius: var(--radius-md); 44 | 45 | --shadow-color: hsla(from var(--shade-color) h s l / 0.39); 46 | box-shadow: 47 | 0.8px 0.7px 1.2px var(--shadow-color), 48 | 2.4px 1.9px 3.4px -0.6px var(--shadow-color), 49 | 4.8px 3.9px 7px -1.2px var(--shadow-color), 50 | 9.9px 7.9px 14.2px -1.9px var(--shadow-color), 51 | 19.2px 15.4px 27.7px -2.5px var(--shadow-color); 52 | } 53 | 54 | main>img { 55 | grid-area: char-img; 56 | height: 13rem; 57 | width: 21rem; 58 | margin: 0.5rem; 59 | 60 | object-fit: cover; 61 | user-select: none; 62 | pointer-events: none; 63 | outline: 0.25rem solid var(--primary); 64 | outline-offset: 0.25rem; 65 | } 66 | 67 | main>h1 { 68 | grid-area: title; 69 | margin: 0; 70 | padding-bottom: 0.75rem; 71 | font-size: 2rem; 72 | letter-spacing: 0.5rem; 73 | line-height: 1; 74 | 75 | color: var(--primary); 76 | text-decoration: underline; 77 | text-decoration-thickness: 0.25rem; 78 | text-underline-offset: 0.5rem; 79 | 80 | filter: drop-shadow(0.125rem 0 var(--secondary)); 81 | } 82 | 83 | main>nav { 84 | grid-area: nav; 85 | display: grid; 86 | grid-template-columns: 1fr 1fr; 87 | column-gap: 2rem; 88 | row-gap: 1rem; 89 | 90 | 91 | label { 92 | display: inline-block; 93 | color: var(--secondary); 94 | font-weight: bold; 95 | max-width: 10rem; 96 | text-overflow: ellipsis; 97 | overflow: hidden; 98 | white-space: nowrap; 99 | } 100 | 101 | li { 102 | height: 1.5rem; 103 | } 104 | 105 | a { 106 | position: relative; 107 | color: transparent; 108 | margin: 0 -0.5rem; 109 | text-decoration: none; 110 | outline: none; 111 | border-radius: 0.125rem; 112 | 113 | &, &::before, &::after { 114 | display: inline-block; 115 | max-width: 10rem; 116 | padding: 0 0.5rem; 117 | overflow: hidden; 118 | text-overflow: ellipsis; 119 | white-space: nowrap; 120 | } 121 | 122 | &::before, &::after { 123 | content: attr(data-text); 124 | color: var(--text); 125 | position: absolute; 126 | left: 0; 127 | transition: 500ms cubic-bezier(0, 1, 0, 0.66); 128 | transition-property: transform, color; 129 | } 130 | &::before { 131 | clip-path: polygon(0% 0%, 100% 0%, 100% 55%, 0% 55%); 132 | } 133 | &::after { 134 | clip-path: polygon(0% 55%, 100% 55%, 100% 100%, 0% 100%); 135 | } 136 | 137 | 138 | &:focus-visible { 139 | outline: var(--focus-ring); 140 | } 141 | 142 | &:hover, &:focus-visible { 143 | &::before { 144 | color: var(--secondary); 145 | transform: translateX(0.25rem) skewX(-15deg); 146 | } 147 | &::after { 148 | color: var(--secondary); 149 | transform: translateX(-0.25rem) skewX(-15deg); 150 | } 151 | } 152 | } 153 | } 154 | 155 | main>form { 156 | grid-area: search; 157 | display: flex; 158 | gap: 1rem; 159 | position: relative; 160 | 161 | &>input { 162 | flex: 1; 163 | padding-right: 2.5rem; 164 | } 165 | &>button { 166 | position: absolute; 167 | right: 0; 168 | } 169 | } 170 | 171 | svg { 172 | width: 1rem; 173 | height: 1rem; 174 | } 175 | 176 | @media screen and (max-width: 56rem) { 177 | body>main { 178 | width: 100%; 179 | max-width: 24rem; 180 | padding: 1rem; 181 | grid-template-areas: 182 | "title" 183 | "char-img" 184 | "search" 185 | "nav"; 186 | 187 | >img { 188 | width: calc(100% - 1rem); 189 | } 190 | nav { 191 | width: 100%; 192 | overflow: hidden; 193 | align-items: stretch; 194 | justify-items: stretch; 195 | justify-content: stretch; 196 | ul, li, a { 197 | overflow: hidden; 198 | text-overflow: ellipsis; 199 | } 200 | a, a::before, a::after { 201 | max-width: 100%; 202 | } 203 | } 204 | } 205 | } 206 | 207 | @media screen and (max-width: 21rem) { 208 | body>main>nav { 209 | grid-template-columns: 1fr; 210 | } 211 | } 212 | 213 | --------------------------------------------------------------------------------