at master 3.6 kB view raw
1--- 2import { urls, type nav } from "/site-config"; 3import boxBlr from "/assets/box-blr.png"; 4import dot from "/assets/dot.png"; 5import boxTlbr from "/assets/box-tlbr.png"; 6 7const betweenArr = <A, I>(arr: A[], item: I): (A | I)[] => 8 arr.flatMap((x, i) => (i !== 0 ? [item, x] : [x])); 9 10const strToX = <I,>( 11 arr: string, 12 map: [string, I][], 13): (string | I)[] | string => { 14 const [key, value] = map.pop() ?? [undefined, undefined]; 15 if (!key) return arr; 16 return betweenArr(arr.split(key), value).flatMap((x: string | I) => 17 typeof x === "string" ? strToX(x, map) : x, 18 ); 19}; 20--- 21 22<header style={`--box-blr-png: url("${boxBlr.src}")`}> 23 <h1>vielle.dev</h1> 24 25 <nav> 26 <ul 27 style={`--dot-png: url("${dot.src}"); 28 --box-tlbr-png: url("${boxTlbr.src}")`} 29 > 30 { 31 (() => { 32 const name = (url: nav) => 33 url.slug ? ( 34 <a href={url.slug}> 35 {strToX(url.name, [ 36 ["&shy;", <>&shy;</>], 37 ["<wbr>", <wbr />], 38 ])} 39 </a> 40 ) : ( 41 url.name 42 ); 43 const flatten = (urls: nav[]) => 44 urls.map((url) => ( 45 <li> 46 {name(url)} 47 {url.children && <ul>{flatten(url.children)}</ul>} 48 </li> 49 )); 50 return urls.map((url) => ( 51 <li> 52 {url.children ? ( 53 <details name="nav"> 54 <summary>{name(url)}</summary> 55 <ul>{flatten(url.children)}</ul> 56 </details> 57 ) : ( 58 name(url) 59 )} 60 </li> 61 )); 62 })() 63 } 64 </ul> 65 </nav> 66</header> 67 68<style> 69 /* UPDATE RSS STYLE XML WHEN YOU CHANGE THESE */ 70 header { 71 border-image: var(--box-blr-png) 10 fill / 20px / 20px round; 72 margin: 0 20px 20px; 73 padding: 10px 20px; 74 height: 3rem; /* 2rem * 1.5 */ 75 /* render over feed bg */ 76 z-index: 2; 77 78 display: flex; 79 flex-direction: row; 80 justify-content: space-between; 81 align-items: center; 82 83 @media (max-width: 650px) { 84 flex-direction: column; 85 align-items: start; 86 height: 4.5rem; /* (2rem + 1rem) * 1.5 */ 87 88 nav { 89 margin-inline: auto; 90 contain: inline-size; 91 width: 100%; 92 overflow: auto; 93 scrollbar-width: thin; 94 } 95 96 h1 { 97 width: 100%; 98 text-align: center; 99 } 100 } 101 102 & > nav > ul { 103 display: flex; 104 flex-direction: row; 105 align-items: center; 106 justify-content: start; 107 gap: 10px; 108 z-index: 999; 109 width: fit-content; 110 margin-inline: auto; 111 112 & > li { 113 display: flex; 114 flex-direction: row; 115 align-items: center; 116 gap: 10px; 117 118 &::marker { 119 content: none; 120 } 121 122 & + &::before { 123 content: ""; 124 background-image: var(--dot-png); 125 background-size: contain; 126 width: 9px; 127 height: 9px; 128 display: block; 129 } 130 } 131 } 132 } 133 134 nav > ul > li:last-child > details > ul { 135 right: 10px; 136 } 137 138 details { 139 summary { 140 cursor: pointer; 141 text-wrap: nowrap; 142 } 143 144 & > ul { 145 position: absolute; 146 z-index: 99999; 147 148 @media (max-width: 650px) { 149 inset: auto 15px; 150 } 151 152 margin-top: 10px; 153 padding: 20px; 154 padding-left: 40px; 155 & ul { 156 margin-left: 10px; 157 } 158 159 border-image: var(--box-tlbr-png) 10 fill / 20px round; 160 } 161 } 162 163 h1 { 164 margin-block: 0; 165 position: sticky; 166 inset: 0; 167 } 168</style>