├── 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 | 
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 |
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 |
--------------------------------------------------------------------------------