an appview-less Bluesky client using Constellation and PDS Queries reddwarf.app
frontend spa bluesky reddwarf microcosm

better settings styles

rimar1337 65f60fa0 fc6b4da8

Changed files
+146 -24
src
routes
styles
+55 -24
src/routes/settings.tsx
··· 1 1 import { createFileRoute } from "@tanstack/react-router"; 2 2 import { useAtom, useAtomValue, useSetAtom } from "jotai"; 3 3 import { Slider, Switch } from "radix-ui"; 4 - import { useEffect,useState } from "react"; 4 + import { useEffect, useState } from "react"; 5 5 6 6 import { Header } from "~/components/Header"; 7 7 import Login from "~/components/Login"; ··· 40 40 <Login /> 41 41 </div> 42 42 <div className="h-4" /> 43 + 44 + <SettingHeading title="Personalization" top /> 45 + <Hue /> 46 + 47 + <SettingHeading title="Network Configuration" /> 48 + <div className="flex flex-col px-4 pb-2"> 49 + <span className="text-md">Service Endpoints</span> 50 + <span className="text-sm text-gray-500 dark:text-gray-400"> 51 + Customize the servers to be used by the app 52 + </span> 53 + </div> 43 54 <TextInputSetting 44 55 atom={constellationURLAtom} 45 56 title={"Constellation"} ··· 69 80 init={defaultVideoCDN} 70 81 /> 71 82 72 - <Hue /> 83 + <SettingHeading title="Experimental" /> 73 84 <SwitchSetting 74 85 atom={enableBitesAtom} 75 86 title={"Bites"} 76 - description={"Enable Wafrn Bites"} 87 + description={"Enable Wafrn Bites to bite other people"} 77 88 //init={false} 78 - /> 79 - <p className="text-gray-500 dark:text-gray-400 py-4 px-6 text-sm"> 80 - please restart/refresh the app if changes arent applying correctly 89 + /> 90 + <p className="text-gray-500 dark:text-gray-400 py-4 px-4 text-sm border rounded-xl mx-4 mt-8 mb-4"> 91 + Notice: Please restart/refresh the app if changes arent applying 92 + correctly 81 93 </p> 82 94 </> 83 95 ); 84 96 } 85 97 98 + export function SettingHeading({ 99 + title, 100 + top, 101 + }: { 102 + title: string; 103 + top?: boolean; 104 + }) { 105 + return ( 106 + <div 107 + className="px-4" 108 + style={{ marginTop: top ? 0 : 18, paddingBottom: 12 }} 109 + > 110 + <span className=" text-sm font-medium text-gray-500 dark:text-gray-400"> 111 + {title} 112 + </span> 113 + </div> 114 + ); 115 + } 116 + 86 117 export function SwitchSetting({ 87 118 atom, 88 119 title, ··· 105 136 } 106 137 107 138 return ( 108 - <div className="flex items-center gap-4 px-4 py-2"> 139 + <div className="flex items-center gap-4 px-4 "> 109 140 <div className="flex flex-col"> 110 - <label htmlFor="switch-demo" className="text-lg"> 141 + <label htmlFor="switch-demo" className="text-md"> 111 142 {title} 112 143 </label> 113 - <span className="text-sm">{description}</span> 144 + <span className="text-sm text-gray-500 dark:text-gray-400"> 145 + {description} 146 + </span> 114 147 </div> 148 + 149 + <div className="flex-1" /> 115 150 116 151 <Switch.Root 117 152 id="switch-demo" 118 153 checked={value} 119 154 onCheckedChange={(v) => setValue(v)} 120 - className="w-10 h-6 bg-gray-300 rounded-full relative data-[state=checked]:bg-blue-500 transition-colors" 155 + className="m3switch root" 121 156 > 122 - <Switch.Thumb 123 - className="block w-5 h-5 bg-white rounded-full shadow-sm transition-transform translate-x-[2px] data-[state=checked]:translate-x-[20px]" 124 - /> 157 + <Switch.Thumb className="m3switch thumb " /> 125 158 </Switch.Root> 126 159 </div> 127 160 ); ··· 130 163 function Hue() { 131 164 const [hue, setHue] = useAtom(hueAtom); 132 165 return ( 133 - <div className="flex flex-col px-4 mt-4 "> 134 - <span className="z-10">Hue</span> 135 - <div className="flex flex-row items-center gap-4"> 136 - <SliderComponent 137 - atom={hueAtom} 138 - max={360} 139 - /> 166 + <div className="flex flex-col px-4"> 167 + <span className="z-[2] text-md">Hue</span> 168 + <span className="z-[2] text-sm text-gray-500 dark:text-gray-400"> 169 + Change the colors of the app 170 + </span> 171 + <div className="z-[1] flex flex-row items-center gap-4"> 172 + <SliderComponent atom={hueAtom} max={360} /> 140 173 <button 141 174 onClick={() => setHue(defaulthue ?? 28)} 142 175 className="px-6 py-2 h-12 rounded-full bg-gray-100 dark:bg-gray-800 ··· 207 240 ); 208 241 } 209 242 210 - 211 243 interface SliderProps { 212 244 atom: typeof hueAtom; 213 245 min?: number; ··· 221 253 max = 100, 222 254 step = 1, 223 255 }) => { 224 - 225 - const [value, setValue] = useAtom(atom) 256 + const [value, setValue] = useAtom(atom); 226 257 227 258 return ( 228 259 <Slider.Root ··· 239 270 <Slider.Thumb className="shadow-[0_0_0_8px_var(--color-white)] dark:shadow-[0_0_0_8px_var(--color-gray-950)] block w-[3px] h-12 bg-gray-500 dark:bg-gray-400 rounded-md focus:outline-none" /> 240 271 </Slider.Root> 241 272 ); 242 - }; 273 + };
+91
src/styles/app.css
··· 277 277 } 278 278 } 279 279 } 280 + } 281 + 282 + :root{ 283 + --thumb-size: 2rem; 284 + --root-size: 3.25rem; 285 + 286 + --switch-off-border: var(--color-gray-400); 287 + --switch-off-bg: var(--color-gray-200); 288 + --switch-off-thumb: var(--color-gray-400); 289 + 290 + 291 + --switch-on-bg: var(--color-gray-500); 292 + --switch-on-thumb: var(--color-gray-50); 293 + 294 + } 295 + @media (prefers-color-scheme: dark) { 296 + :root { 297 + --switch-off-border: var(--color-gray-500); 298 + --switch-off-bg: var(--color-gray-800); 299 + --switch-off-thumb: var(--color-gray-500); 300 + 301 + 302 + --switch-on-bg: var(--color-gray-400); 303 + --switch-on-thumb: var(--color-gray-700); 304 + } 305 + } 306 + 307 + .m3switch.root{ 308 + /*w-10 h-6 bg-gray-300 rounded-full relative data-[state=checked]:bg-gray-500 transition-colors*/ 309 + /*width: 40px; 310 + height: 24px;*/ 311 + 312 + inline-size: var(--root-size); 313 + block-size: 2rem; 314 + border-radius: 99999px; 315 + 316 + transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to; 317 + transition-timing-function: var(--default-transition-timing-function); /* cubic-bezier(0.4, 0, 0.2, 1) */ 318 + transition-duration: var(--default-transition-duration); /* 150ms */ 319 + 320 + .m3switch.thumb{ 321 + /*block w-5 h-5 bg-white rounded-full shadow-sm transition-transform translate-x-[2px] data-[state=checked]:translate-x-[20px]*/ 322 + 323 + height: var(--thumb-size); 324 + width: var(--thumb-size); 325 + display: inline-block; 326 + border-radius: 9999px; 327 + 328 + transform-origin: center; 329 + 330 + transition-property: transform, translate, scale, rotate; 331 + transition-timing-function: var(--default-transition-timing-function); /* cubic-bezier(0.4, 0, 0.2, 1) */ 332 + transition-duration: var(--default-transition-duration); /* 150ms */ 333 + 334 + } 335 + 336 + &[aria-checked="true"] { 337 + box-shadow: inset 0px 0px 0px 1.8px transparent; 338 + background-color: var(--switch-on-bg); 339 + 340 + .m3switch.thumb{ 341 + /*block w-5 h-5 bg-white rounded-full shadow-sm transition-transform translate-x-[2px] data-[state=checked]:translate-x-[20px]*/ 342 + 343 + background-color: var(--switch-on-thumb); 344 + transform: translate(calc((var(--root-size) / 2) - 50%),0) scale(0.72); 345 + &:active { 346 + transform: translate(calc((var(--root-size) / 2) - 50%),0) scale(0.88); 347 + } 348 + 349 + } 350 + &:active .m3switch.thumb{ 351 + transform: translate(calc((var(--root-size) / 2) - 50%),0) scale(0.88); 352 + } 353 + } 354 + 355 + &[aria-checked="false"] { 356 + box-shadow: inset 0px 0px 0px 1.8px var(--switch-off-border); 357 + background-color: var(--switch-off-bg); 358 + .m3switch.thumb{ 359 + /*block w-5 h-5 bg-white rounded-full shadow-sm transition-transform translate-x-[2px] data-[state=checked]:translate-x-[20px]*/ 360 + 361 + background-color: var(--switch-off-thumb); 362 + transform: translate(calc(-1 * (var(--root-size) / 2) + 50%),0) scale(0.5); 363 + &:active { 364 + transform: translate(calc(-1 * (var(--root-size) / 2) + 50%),0) scale(0.88); 365 + } 366 + } 367 + &:active .m3switch.thumb{ 368 + transform: translate(calc(-1 * (var(--root-size) / 2) + 50%),0) scale(0.88); 369 + } 370 + } 280 371 }