authorgravatar for maria@kuuro.netmaria-rcks <maria@kuuro.net> 2026-07-22 22:38:47-04:00
committergravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-07-23 01:13:29-04:00
log0b1ce588246c1e38124598d9fcac4bc5e4063ce3
treed96405307fa67508cefae1c3ac43ca4c36a29667
parent10da67b9da3e366acf827518423978e4d762a337

style(web): polish glass opacity slider


2 files changed, 111 insertions(+), 10 deletions(-)

apps/web/src/components/settings/SettingsPanels.tsx+14-6
......@@ -1,5 +1,6 @@
11import { ArchiveIcon, ArchiveX, LoaderIcon, PlusIcon, RefreshCwIcon } from "lucide-react";
22import { Link } from "@tanstack/react-router";
3import type { CSSProperties } from "react";
34import { useCallback, useMemo, useRef, useState } from "react";
45import { useAtomValue } from "@effect/atom-react";
56import {
......@@ -499,6 +500,12 @@ export function GeneralSettingsPanel() {
499500 const updateSettings = useUpdatePrimarySettings();
500501 const observability = useAtomValue(primaryServerObservabilityAtom);
501502 const serverProviders = useAtomValue(primaryServerProvidersAtom);
503 const glassOpacityRatio =
504 (settings.glassOpacity - MIN_GLASS_OPACITY) / (MAX_GLASS_OPACITY - MIN_GLASS_OPACITY);
505 const glassOpacitySliderStyle = {
506 "--glass-slider-progress": `${glassOpacityRatio * 100}%`,
507 "--glass-slider-fill-offset": `${0.5 - glassOpacityRatio}rem`,
508 } as CSSProperties;
502509 const diagnosticsDescription = formatDiagnosticsDescription({
503510 localTracingEnabled: observability?.localTracingEnabled ?? false,
504511 otlpTracesEnabled: observability?.otlpTracesEnabled ?? false,
......@@ -581,6 +588,12 @@ export function GeneralSettingsPanel() {
581588 }
582589 control={
583590 <div className="flex w-full items-center gap-3 sm:w-52">
591 <output
592 className="min-w-12 rounded-md bg-muted px-2 py-1 text-center font-mono text-xs font-medium tabular-nums text-foreground"
593 htmlFor="glass-opacity"
594 >
595 {settings.glassOpacity}%
596 </output>
584597 <input
585598 aria-label="Glass opacity"
586599 className="glass-opacity-slider min-w-0 flex-1"
......@@ -598,15 +611,10 @@ export function GeneralSettingsPanel() {
598611 }
599612 }}
600613 step={5}
614 style={glassOpacitySliderStyle}
601615 type="range"
602616 value={settings.glassOpacity}
603617 />
604 <output
605 className="w-10 text-right font-mono text-xs tabular-nums text-muted-foreground"
606 htmlFor="glass-opacity"
607 >
608 {settings.glassOpacity}%
609 </output>
610618 </div>
611619 }
612620 />
apps/web/src/index.css+97-4
......@@ -450,14 +450,107 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
450450 }
451451
452452 .glass-opacity-slider {
453 accent-color: var(--primary);
453 --glass-slider-progress: 0%;
454 --glass-slider-fill-offset: 0.5rem;
455 --glass-slider-fill-position: calc(
456 var(--glass-slider-progress) + var(--glass-slider-fill-offset)
457 );
458 appearance: none;
459 height: 1.5rem;
460 background: transparent;
454461 cursor: pointer;
455462 }
456463
457 .glass-opacity-slider:focus-visible {
464 .glass-opacity-slider::-webkit-slider-runnable-track {
465 height: 0.375rem;
458466 border-radius: 9999px;
459 outline: 2px solid var(--ring);
460 outline-offset: 3px;
467 background: linear-gradient(
468 to right,
469 var(--primary) 0 var(--glass-slider-fill-position),
470 color-mix(in srgb, var(--muted-foreground) 22%, transparent) var(--glass-slider-fill-position)
471 100%
472 );
473 box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border) 55%, transparent);
474 }
475
476 .glass-opacity-slider::-moz-range-track {
477 height: 0.375rem;
478 border-radius: 9999px;
479 background: color-mix(in srgb, var(--muted-foreground) 22%, transparent);
480 box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border) 55%, transparent);
481 }
482
483 .glass-opacity-slider::-moz-range-progress {
484 height: 0.375rem;
485 border-radius: 9999px;
486 background: var(--primary);
487 }
488
489 .glass-opacity-slider::-webkit-slider-thumb {
490 appearance: none;
491 width: 1rem;
492 height: 1rem;
493 margin-top: -0.3125rem;
494 border: 2px solid var(--primary);
495 border-radius: 9999px;
496 background: var(--background);
497 box-shadow: 0 1px 2px color-mix(in srgb, var(--foreground) 12%, transparent);
498 transition:
499 transform 120ms ease,
500 box-shadow 120ms ease;
501 }
502
503 .glass-opacity-slider::-moz-range-thumb {
504 width: 1rem;
505 height: 1rem;
506 border: 2px solid var(--primary);
507 border-radius: 9999px;
508 background: var(--background);
509 box-shadow: 0 1px 2px color-mix(in srgb, var(--foreground) 12%, transparent);
510 transition:
511 transform 120ms ease,
512 box-shadow 120ms ease;
513 }
514
515 .glass-opacity-slider:hover::-webkit-slider-thumb {
516 transform: scale(1.08);
517 box-shadow: 0 1px 3px color-mix(in srgb, var(--foreground) 20%, transparent);
518 }
519
520 .glass-opacity-slider:hover::-moz-range-thumb {
521 transform: scale(1.08);
522 box-shadow: 0 1px 3px color-mix(in srgb, var(--foreground) 20%, transparent);
523 }
524
525 .glass-opacity-slider:active::-webkit-slider-thumb {
526 transform: scale(0.94);
527 }
528
529 .glass-opacity-slider:active::-moz-range-thumb {
530 transform: scale(0.94);
531 }
532
533 .glass-opacity-slider:focus-visible {
534 outline: none;
535 }
536
537 .glass-opacity-slider:focus-visible::-webkit-slider-thumb {
538 box-shadow:
539 0 0 0 3px var(--background),
540 0 0 0 5px var(--ring);
541 }
542
543 .glass-opacity-slider:focus-visible::-moz-range-thumb {
544 box-shadow:
545 0 0 0 3px var(--background),
546 0 0 0 5px var(--ring);
547 }
548
549 @media (forced-colors: active) {
550 .glass-opacity-slider {
551 appearance: auto;
552 accent-color: Highlight;
553 }
461554 }
462555
463556 @media (min-width: 40rem) {