Compare commits
7 Commits
223dff2cda
...
d749f86edc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d749f86edc | ||
|
|
8aad8942fc | ||
|
|
0eebe03bf8 | ||
|
|
2508168a3e | ||
|
|
a557e15759 | ||
|
|
a5b9238306 | ||
|
|
f01299f3d1 |
@@ -37,6 +37,26 @@
|
|||||||
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
||||||
--sidebar-border: oklch(0.92 0.004 286.32);
|
--sidebar-border: oklch(0.92 0.004 286.32);
|
||||||
--sidebar-ring: oklch(0.705 0.015 286.067);
|
--sidebar-ring: oklch(0.705 0.015 286.067);
|
||||||
|
|
||||||
|
--background-20: oklch(1 0 0 / 20%);
|
||||||
|
--background-40: oklch(1 0 0 / 40%);
|
||||||
|
--background-60: oklch(1 0 0 / 60%);
|
||||||
|
--background-80: oklch(1 0 0 / 80%);
|
||||||
|
--background-95: oklch(1 0 0 / 95%);
|
||||||
|
--background-subtle: oklch(0.98 0 0);
|
||||||
|
--background-muted: oklch(0.97 0.002 286.375);
|
||||||
|
|
||||||
|
--text-muted: oklch(0.552 0.016 285.938);
|
||||||
|
--text-subtle: oklch(0.705 0.015 286.067);
|
||||||
|
--text-soft: oklch(0.5 0.01 286);
|
||||||
|
|
||||||
|
--border-subtle: oklch(0.95 0.003 286.32);
|
||||||
|
--border-muted: oklch(0.92 0.004 286.32);
|
||||||
|
--border-soft: oklch(0.88 0.005 286.32);
|
||||||
|
|
||||||
|
--gradient-from: oklch(0.98 0.002 286.32);
|
||||||
|
--gradient-via: oklch(1 0 0);
|
||||||
|
--gradient-to: oklch(0.98 0.002 286.32);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
@@ -71,6 +91,26 @@
|
|||||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-border: oklch(1 0 0 / 10%);
|
--sidebar-border: oklch(1 0 0 / 10%);
|
||||||
--sidebar-ring: oklch(0.552 0.016 285.938);
|
--sidebar-ring: oklch(0.552 0.016 285.938);
|
||||||
|
|
||||||
|
--background-20: oklch(0.21 0.006 285.885 / 20%);
|
||||||
|
--background-40: oklch(0.21 0.006 285.885 / 40%);
|
||||||
|
--background-60: oklch(0.21 0.006 285.885 / 60%);
|
||||||
|
--background-80: oklch(0.21 0.006 285.885 / 80%);
|
||||||
|
--background-95: oklch(0.21 0.006 285.885 / 95%);
|
||||||
|
--background-subtle: oklch(0.18 0.005 285.823);
|
||||||
|
--background-muted: oklch(0.274 0.006 286.033);
|
||||||
|
|
||||||
|
--text-muted: oklch(0.705 0.015 286.067);
|
||||||
|
--text-subtle: oklch(0.552 0.016 285.938);
|
||||||
|
--text-soft: oklch(0.8 0.01 286);
|
||||||
|
|
||||||
|
--border-subtle: oklch(1 0 0 / 8%);
|
||||||
|
--border-muted: oklch(1 0 0 / 10%);
|
||||||
|
--border-soft: oklch(1 0 0 / 15%);
|
||||||
|
|
||||||
|
--gradient-from: oklch(0.25 0.005 285.885);
|
||||||
|
--gradient-via: oklch(0.21 0.006 285.885);
|
||||||
|
--gradient-to: oklch(0.25 0.005 285.885);
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
@@ -109,6 +149,22 @@
|
|||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
|
--color-background-20: var(--background-20);
|
||||||
|
--color-background-40: var(--background-40);
|
||||||
|
--color-background-60: var(--background-60);
|
||||||
|
--color-background-80: var(--background-80);
|
||||||
|
--color-background-95: var(--background-95);
|
||||||
|
--color-background-subtle: var(--background-subtle);
|
||||||
|
--color-background-muted: var(--background-muted);
|
||||||
|
--color-text-muted: var(--text-muted);
|
||||||
|
--color-text-subtle: var(--text-subtle);
|
||||||
|
--color-text-soft: var(--text-soft);
|
||||||
|
--color-border-subtle: var(--border-subtle);
|
||||||
|
--color-border-muted: var(--border-muted);
|
||||||
|
--color-border-soft: var(--border-soft);
|
||||||
|
--color-gradient-from: var(--gradient-from);
|
||||||
|
--color-gradient-via: var(--gradient-via);
|
||||||
|
--color-gradient-to: var(--gradient-to);
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from 'svelte';
|
||||||
|
|
||||||
export interface BreadcrumbItem {
|
export interface BreadcrumbItem {
|
||||||
|
/**
|
||||||
|
* Index of the item to display
|
||||||
|
*/
|
||||||
index: number;
|
index: number;
|
||||||
|
/**
|
||||||
|
* ID of the item to navigate to
|
||||||
|
*/
|
||||||
|
id?: string;
|
||||||
|
/**
|
||||||
|
* Title snippet to render
|
||||||
|
*/
|
||||||
title: Snippet<[{ className?: string }]>;
|
title: Snippet<[{ className?: string }]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
Fixed header for breadcrumbs navigation for sections in the page
|
Fixed header for breadcrumbs navigation for sections in the page
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { smoothScroll } from '$shared/lib';
|
||||||
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
||||||
import {
|
import {
|
||||||
fly,
|
fly,
|
||||||
@@ -16,8 +17,8 @@ import { scrollBreadcrumbsStore } from '../../model';
|
|||||||
transition:slide={{ duration: 200 }}
|
transition:slide={{ duration: 200 }}
|
||||||
class="
|
class="
|
||||||
fixed top-0 left-0 right-0 z-100
|
fixed top-0 left-0 right-0 z-100
|
||||||
backdrop-blur-lg bg-white/20
|
backdrop-blur-lg bg-background-20
|
||||||
border-b border-gray-300/50
|
border-b border-border-muted
|
||||||
shadow-[0_1px_3px_rgba(0,0,0,0.04)]
|
shadow-[0_1px_3px_rgba(0,0,0,0.04)]
|
||||||
h-10 sm:h-12
|
h-10 sm:h-12
|
||||||
"
|
"
|
||||||
@@ -27,7 +28,7 @@ import { scrollBreadcrumbsStore } from '../../model';
|
|||||||
GLYPHDIFF
|
GLYPHDIFF
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div class="h-3.5 sm:h-4 w-px bg-gray-300/60 hidden sm:block"></div>
|
<div class="h-3.5 sm:h-4 w-px bg-border-subtle hidden sm:block"></div>
|
||||||
|
|
||||||
<nav class="flex items-center gap-2 sm:gap-3 overflow-x-auto scrollbar-hide flex-1">
|
<nav class="flex items-center gap-2 sm:gap-3 overflow-x-auto scrollbar-hide flex-1">
|
||||||
{#each scrollBreadcrumbsStore.items as item, idx (item.index)}
|
{#each scrollBreadcrumbsStore.items as item, idx (item.index)}
|
||||||
@@ -36,19 +37,19 @@ import { scrollBreadcrumbsStore } from '../../model';
|
|||||||
out:fly={{ duration: 300, y: 10, x: 100, opacity: 0 }}
|
out:fly={{ duration: 300, y: 10, x: 100, opacity: 0 }}
|
||||||
class="flex items-center gap-2 sm:gap-3 whitespace-nowrap shrink-0"
|
class="flex items-center gap-2 sm:gap-3 whitespace-nowrap shrink-0"
|
||||||
>
|
>
|
||||||
<span class="font-mono text-[8px] sm:text-[9px] text-gray-400 tracking-wider">
|
<span class="font-mono text-[8px] sm:text-[9px] text-text-muted tracking-wider">
|
||||||
{String(item.index).padStart(2, '0')}
|
{String(item.index).padStart(2, '0')}
|
||||||
</span>
|
</span>
|
||||||
|
<a href={`#${item.id}`} use:smoothScroll>
|
||||||
{@render item.title({
|
{@render item.title({
|
||||||
className: 'text-[9px] sm:text-[10px] font-bold uppercase tracking-tight leading-[0.95] text-gray-900',
|
className: 'text-[9px] sm:text-[10px] font-bold uppercase tracking-tight leading-[0.95] text-foreground',
|
||||||
})}
|
})}</a>
|
||||||
|
|
||||||
{#if idx < scrollBreadcrumbsStore.items.length - 1}
|
{#if idx < scrollBreadcrumbsStore.items.length - 1}
|
||||||
<div class="flex items-center gap-0.5 opacity-40">
|
<div class="flex items-center gap-0.5 opacity-40">
|
||||||
<div class="w-1 h-px bg-gray-400"></div>
|
<div class="w-1 h-px bg-text-muted"></div>
|
||||||
<div class="w-1 h-px bg-gray-400"></div>
|
<div class="w-1 h-px bg-text-muted"></div>
|
||||||
<div class="w-1 h-px bg-gray-400"></div>
|
<div class="w-1 h-px bg-text-muted"></div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -56,8 +57,8 @@ import { scrollBreadcrumbsStore } from '../../model';
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="flex items-center gap-1.5 sm:gap-2 opacity-50 ml-auto">
|
<div class="flex items-center gap-1.5 sm:gap-2 opacity-50 ml-auto">
|
||||||
<div class="w-px h-2 sm:h-2.5 bg-gray-300/60 hidden sm:block"></div>
|
<div class="w-px h-2 sm:h-2.5 bg-border-subtle hidden sm:block"></div>
|
||||||
<span class="font-mono text-[7px] sm:text-[8px] text-gray-400 tracking-wider">
|
<span class="font-mono text-[7px] sm:text-[8px] text-text-muted tracking-wider">
|
||||||
[{scrollBreadcrumbsStore.items.length}]
|
[{scrollBreadcrumbsStore.items.length}]
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ function handleNearBottom(lastVisibleIndex: number) {
|
|||||||
{#if isLoading}
|
{#if isLoading}
|
||||||
<div class="flex flex-col gap-3 sm:gap-4 p-3 sm:p-4">
|
<div class="flex flex-col gap-3 sm:gap-4 p-3 sm:p-4">
|
||||||
{#each Array(5) as _, i}
|
{#each Array(5) as _, i}
|
||||||
<div class="flex flex-col gap-1.5 sm:gap-2 p-3 sm:p-4 border rounded-lg sm:rounded-xl border-gray-200/50 bg-white/40">
|
<div class="flex flex-col gap-1.5 sm:gap-2 p-3 sm:p-4 border rounded-lg sm:rounded-xl border-border-subtle bg-background-40">
|
||||||
<div class="flex items-center justify-between mb-3 sm:mb-4">
|
<div class="flex items-center justify-between mb-3 sm:mb-4">
|
||||||
<Skeleton class="h-6 sm:h-8 w-1/3" />
|
<Skeleton class="h-6 sm:h-8 w-1/3" />
|
||||||
<Skeleton class="h-6 sm:h-8 w-6 sm:w-8 rounded-full" />
|
<Skeleton class="h-6 sm:h-8 w-6 sm:w-8 rounded-full" />
|
||||||
|
|||||||
@@ -53,20 +53,20 @@ const letterSpacing = $derived(controlManager.spacing);
|
|||||||
class="
|
class="
|
||||||
w-full h-full rounded-xl sm:rounded-2xl
|
w-full h-full rounded-xl sm:rounded-2xl
|
||||||
flex flex-col
|
flex flex-col
|
||||||
backdrop-blur-md bg-white/80
|
backdrop-blur-md bg-background-80
|
||||||
border border-gray-300/50
|
border border-border-muted
|
||||||
shadow-[0_1px_3px_rgba(0,0,0,0.04)]
|
shadow-[0_1px_3px_rgba(0,0,0,0.04)]
|
||||||
relative overflow-hidden
|
relative overflow-hidden
|
||||||
"
|
"
|
||||||
style:font-weight={fontWeight}
|
style:font-weight={fontWeight}
|
||||||
>
|
>
|
||||||
<div class="px-4 sm:px-5 md:px-6 py-2.5 sm:py-3 border-b border-gray-200/60 flex items-center justify-between">
|
<div class="px-4 sm:px-5 md:px-6 py-2.5 sm:py-3 border-b border-border-subtle flex items-center justify-between">
|
||||||
<div class="flex items-center gap-2 sm:gap-2.5">
|
<div class="flex items-center gap-2 sm:gap-2.5">
|
||||||
<Footnote>
|
<Footnote>
|
||||||
typeface_{String(index).padStart(3, '0')}
|
typeface_{String(index).padStart(3, '0')}
|
||||||
</Footnote>
|
</Footnote>
|
||||||
<div class="w-px h-2 sm:h-2.5 bg-gray-300/60"></div>
|
<div class="w-px h-2 sm:h-2.5 bg-border-subtle"></div>
|
||||||
<Footnote class="tracking-[0.15em] font-bold text-gray-900">
|
<Footnote class="tracking-[0.15em] font-bold text-foreground">
|
||||||
{font.name}
|
{font.name}
|
||||||
</Footnote>
|
</Footnote>
|
||||||
</div>
|
</div>
|
||||||
@@ -95,19 +95,19 @@ const letterSpacing = $derived(controlManager.spacing);
|
|||||||
</FontApplicator>
|
</FontApplicator>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-4 sm:px-5 md:px-6 py-1.5 sm:py-2 border-t border-gray-200/40 w-full flex flex-row gap-2 sm:gap-4 bg-gray-50/30 mt-auto">
|
<div class="px-4 sm:px-5 md:px-6 py-1.5 sm:py-2 border-t border-border-subtle w-full flex flex-row gap-2 sm:gap-4 bg-background mt-auto">
|
||||||
<Footnote class="text-[7px] sm:text-[8px] tracking-wider ml-auto">
|
<Footnote class="text-[7px] sm:text-[8px] tracking-wider ml-auto">
|
||||||
SZ:{fontSize}PX
|
SZ:{fontSize}PX
|
||||||
</Footnote>
|
</Footnote>
|
||||||
<div class="w-px h-2 sm:h-2.5 self-center bg-gray-300/60 hidden sm:block"></div>
|
<div class="w-px h-2 sm:h-2.5 self-center bg-border-subtle hidden sm:block"></div>
|
||||||
<Footnote class="text-[7px] sm:text-[8px] tracking-wider">
|
<Footnote class="text-[7px] sm:text-[8px] tracking-wider">
|
||||||
WGT:{fontWeight}
|
WGT:{fontWeight}
|
||||||
</Footnote>
|
</Footnote>
|
||||||
<div class="w-px h-2 sm:h-2.5 self-center bg-gray-300/60 hidden sm:block"></div>
|
<div class="w-px h-2 sm:h-2.5 self-center bg-border-subtle hidden sm:block"></div>
|
||||||
<Footnote class="text-[7px] sm:text-[8px] tracking-wider">
|
<Footnote class="text-[7px] sm:text-[8px] tracking-wider">
|
||||||
LH:{lineHeight?.toFixed(2)}
|
LH:{lineHeight?.toFixed(2)}
|
||||||
</Footnote>
|
</Footnote>
|
||||||
<div class="w-px h-2 sm:h-2.5 self-center bg-gray-300/60 hidden sm:block"></div>
|
<div class="w-px h-2 sm:h-2.5 self-center bg-border-subtle hidden sm:block"></div>
|
||||||
<Footnote class="text-[0.4375rem] sm:text-[0.5rem] tracking-wider">
|
<Footnote class="text-[0.4375rem] sm:text-[0.5rem] tracking-wider">
|
||||||
LTR:{letterSpacing}
|
LTR:{letterSpacing}
|
||||||
</Footnote>
|
</Footnote>
|
||||||
|
|||||||
@@ -23,9 +23,14 @@ let searchContainer: HTMLElement;
|
|||||||
|
|
||||||
let isExpanded = $state(false);
|
let isExpanded = $state(false);
|
||||||
|
|
||||||
function handleTitleStatusChanged(index: number, isPast: boolean, title?: Snippet<[{ className?: string }]>) {
|
function handleTitleStatusChanged(
|
||||||
|
index: number,
|
||||||
|
isPast: boolean,
|
||||||
|
title?: Snippet<[{ className?: string }]>,
|
||||||
|
id?: string,
|
||||||
|
) {
|
||||||
if (isPast && title) {
|
if (isPast && title) {
|
||||||
scrollBreadcrumbsStore.add({ index, title });
|
scrollBreadcrumbsStore.add({ index, title, id });
|
||||||
} else {
|
} else {
|
||||||
scrollBreadcrumbsStore.remove(index);
|
scrollBreadcrumbsStore.remove(index);
|
||||||
}
|
}
|
||||||
@@ -50,7 +55,7 @@ function handleTitleStatusChanged(index: number, isPast: boolean, title?: Snippe
|
|||||||
class="p-2 sm:p-3 md:p-4 h-full flex flex-col gap-3 sm:gap-4"
|
class="p-2 sm:p-3 md:p-4 h-full flex flex-col gap-3 sm:gap-4"
|
||||||
in:fade={{ duration: 500, delay: 150, easing: cubicIn }}
|
in:fade={{ duration: 500, delay: 150, easing: cubicIn }}
|
||||||
>
|
>
|
||||||
<Section class="my-4 sm:my-10 md:my-12 gap-6 sm:gap-8" onTitleStatusChange={handleTitleStatusChanged}>
|
<Section class="py-4 sm:py-10 md:py-12 gap-6 sm:gap-8" onTitleStatusChange={handleTitleStatusChanged}>
|
||||||
{#snippet icon({ className })}
|
{#snippet icon({ className })}
|
||||||
<CodeIcon class={className} />
|
<CodeIcon class={className} />
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@@ -62,7 +67,12 @@ function handleTitleStatusChanged(index: number, isPast: boolean, title?: Snippe
|
|||||||
<Logo />
|
<Logo />
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section class="my-12 gap-8" index={1} onTitleStatusChange={handleTitleStatusChanged}>
|
<Section
|
||||||
|
class="py-4 sm:py-10 md:py-12 gap-6 sm:gap-8"
|
||||||
|
index={1}
|
||||||
|
id="optical_comparator"
|
||||||
|
onTitleStatusChange={handleTitleStatusChanged}
|
||||||
|
>
|
||||||
{#snippet icon({ className })}
|
{#snippet icon({ className })}
|
||||||
<EyeIcon class={className} />
|
<EyeIcon class={className} />
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@@ -74,7 +84,12 @@ function handleTitleStatusChanged(index: number, isPast: boolean, title?: Snippe
|
|||||||
<ComparisonSlider />
|
<ComparisonSlider />
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section class="my-4 sm:my-10 md:my-12 gap-6 sm:gap-8" index={2} onTitleStatusChange={handleTitleStatusChanged}>
|
<Section
|
||||||
|
class="py-4 sm:py-10 md:py-12 gap-6 sm:gap-8"
|
||||||
|
index={2}
|
||||||
|
id="query_module"
|
||||||
|
onTitleStatusChange={handleTitleStatusChanged}
|
||||||
|
>
|
||||||
{#snippet icon({ className })}
|
{#snippet icon({ className })}
|
||||||
<ScanSearchIcon class={className} />
|
<ScanSearchIcon class={className} />
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@@ -86,7 +101,12 @@ function handleTitleStatusChanged(index: number, isPast: boolean, title?: Snippe
|
|||||||
<FontSearch bind:showFilters={isExpanded} />
|
<FontSearch bind:showFilters={isExpanded} />
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section class="my-4 sm:my-10 md:my-12 gap-6 sm:gap-8" index={3} onTitleStatusChange={handleTitleStatusChanged}>
|
<Section
|
||||||
|
class="py-4 sm:py-10 md:py-12 gap-6 sm:gap-8"
|
||||||
|
index={3}
|
||||||
|
id="sample_set"
|
||||||
|
onTitleStatusChange={handleTitleStatusChanged}
|
||||||
|
>
|
||||||
{#snippet icon({ className })}
|
{#snippet icon({ className })}
|
||||||
<LineSquiggleIcon class={className} />
|
<LineSquiggleIcon class={className} />
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|||||||
@@ -24,7 +24,14 @@ export {
|
|||||||
type VirtualizerOptions,
|
type VirtualizerOptions,
|
||||||
} from './helpers';
|
} from './helpers';
|
||||||
|
|
||||||
export { splitArray } from './utils';
|
export {
|
||||||
|
buildQueryString,
|
||||||
|
clampNumber,
|
||||||
|
getDecimalPlaces,
|
||||||
|
roundToStepPrecision,
|
||||||
|
smoothScroll,
|
||||||
|
splitArray,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
export { springySlideFade } from './transitions';
|
export { springySlideFade } from './transitions';
|
||||||
|
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ export { clampNumber } from './clampNumber/clampNumber';
|
|||||||
export { debounce } from './debounce/debounce';
|
export { debounce } from './debounce/debounce';
|
||||||
export { getDecimalPlaces } from './getDecimalPlaces/getDecimalPlaces';
|
export { getDecimalPlaces } from './getDecimalPlaces/getDecimalPlaces';
|
||||||
export { roundToStepPrecision } from './roundToStepPrecision/roundToStepPrecision';
|
export { roundToStepPrecision } from './roundToStepPrecision/roundToStepPrecision';
|
||||||
|
export { smoothScroll } from './smoothScroll/smoothScroll';
|
||||||
export { splitArray } from './splitArray/splitArray';
|
export { splitArray } from './splitArray/splitArray';
|
||||||
|
|||||||
32
src/shared/lib/utils/smoothScroll/smoothScroll.ts
Normal file
32
src/shared/lib/utils/smoothScroll/smoothScroll.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Smoothly scrolls to the target element when an anchor element is clicked.
|
||||||
|
* @param node - The anchor element to listen for clicks on.
|
||||||
|
*/
|
||||||
|
export function smoothScroll(node: HTMLAnchorElement) {
|
||||||
|
const handleClick = (event: MouseEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const hash = node.getAttribute('href');
|
||||||
|
if (!hash || hash === '#') return;
|
||||||
|
|
||||||
|
const targetElement = document.querySelector(hash);
|
||||||
|
|
||||||
|
if (targetElement) {
|
||||||
|
targetElement.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'start',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update URL hash without jumping
|
||||||
|
history.pushState(null, '', hash);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
node.addEventListener('click', handleClick);
|
||||||
|
|
||||||
|
return {
|
||||||
|
destroy() {
|
||||||
|
node.removeEventListener('click', handleClick);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -44,7 +44,7 @@ let {
|
|||||||
<SliderPrimitive.Thumb
|
<SliderPrimitive.Thumb
|
||||||
data-slot="slider-thumb"
|
data-slot="slider-thumb"
|
||||||
index={thumb}
|
index={thumb}
|
||||||
class="border-primary ring-ring/50 block size-4 shrink-0 rounded-full border bg-white shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
class="border-primary ring-ring/50 block size-4 shrink-0 rounded-full border bg-background shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ const handleSliderChange = (newValue: number) => {
|
|||||||
<Button
|
<Button
|
||||||
{...props}
|
{...props}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="hover:bg-white/50 hover:font-bold bg-white/20 border-none duration-150 will-change-transform active:scale-95 cursor-pointer"
|
class="hover:bg-background-50 hover:font-bold bg-background-20 border-none duration-150 will-change-transform active:scale-95 cursor-pointer"
|
||||||
size="icon"
|
size="icon"
|
||||||
aria-label={controlLabel}
|
aria-label={controlLabel}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ function calculateScale(index: number): number | string {
|
|||||||
<div
|
<div
|
||||||
class={cn(
|
class={cn(
|
||||||
'flex gap-4 sm:py-4 sm:px-1 rounded-xl transition-all duration-300',
|
'flex gap-4 sm:py-4 sm:px-1 rounded-xl transition-all duration-300',
|
||||||
'backdrop-blur-md',
|
'',
|
||||||
orientation === 'horizontal' ? 'flex-row items-end w-full' : 'flex-col items-center h-full',
|
orientation === 'horizontal' ? 'flex-row items-end w-full' : 'flex-col items-center h-full',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
@@ -130,10 +130,10 @@ function calculateScale(index: number): number | string {
|
|||||||
orientation === 'horizontal' ? 'flex-col' : 'flex-row',
|
orientation === 'horizontal' ? 'flex-col' : 'flex-row',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span class="font-mono text-[0.375rem] text-gray-400 tabular-nums">
|
<span class="font-mono text-[0.375rem] text-text-muted tabular-nums">
|
||||||
{calculateScale(i)}
|
{calculateScale(i)}
|
||||||
</span>
|
</span>
|
||||||
<div class={cn('bg-gray-300', orientation === 'horizontal' ? 'w-px h-1' : 'h-px w-1')}>
|
<div class={cn('bg-border-muted', orientation === 'horizontal' ? 'w-px h-1' : 'h-px w-1')}>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -163,9 +163,9 @@ function calculateScale(index: number): number | string {
|
|||||||
|
|
||||||
{#if label}
|
{#if label}
|
||||||
<div class="flex items-center gap-2 opacity-70">
|
<div class="flex items-center gap-2 opacity-70">
|
||||||
<div class="w-1 h-1 rounded-full bg-gray-900"></div>
|
<div class="w-1 h-1 rounded-full bg-foreground"></div>
|
||||||
<div class="w-px h-2 bg-gray-400/50"></div>
|
<div class="w-px h-2 bg-text-muted/50"></div>
|
||||||
<span class="font-mono text-[8px] uppercase tracking-[0.2em] text-gray-500 font-medium">
|
<span class="font-mono text-[8px] uppercase tracking-[0.2em] text-text-subtle font-medium">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -195,7 +195,7 @@ function calculateScale(index: number): number | string {
|
|||||||
<Button
|
<Button
|
||||||
{...props}
|
{...props}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="hover:bg-white/50 hover:font-bold bg-white/20 border-none duration-150 will-change-transform active:scale-95 cursor-pointer"
|
class="hover:bg-background-50 hover:font-bold bg-background-20 border-none duration-150 will-change-transform active:scale-95 cursor-pointer"
|
||||||
size="icon"
|
size="icon"
|
||||||
aria-label={controlLabel}
|
aria-label={controlLabel}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -199,8 +199,8 @@ $effect(() => {
|
|||||||
class={cn(
|
class={cn(
|
||||||
'relative p-0.5 sm:p-2 rounded-lg sm:rounded-2xl border transition-all duration-250 ease-out flex flex-col gap-1.5 backdrop-blur-lg',
|
'relative p-0.5 sm:p-2 rounded-lg sm:rounded-2xl border transition-all duration-250 ease-out flex flex-col gap-1.5 backdrop-blur-lg',
|
||||||
expanded
|
expanded
|
||||||
? 'bg-white/5 border-indigo-400/40 shadow-[0_30px_70px_-10px_rgba(99,102,241,0.25)]'
|
? 'bg-background-20 border-indigo-400/40 shadow-[0_30px_70px_-10px_rgba(99,102,241,0.25)]'
|
||||||
: ' bg-white/25 border-white/40 shadow-[0_12px_40px_-12px_rgba(0,0,0,0.12)]',
|
: 'bg-background-40 border-background-40 shadow-[0_12px_40px_-12px_rgba(0,0,0,0.12)]',
|
||||||
disabled && 'opacity-80 grayscale-[0.2]',
|
disabled && 'opacity-80 grayscale-[0.2]',
|
||||||
containerClassName,
|
containerClassName,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ interface Props {
|
|||||||
|
|
||||||
const { children, class: className, render }: Props = $props();
|
const { children, class: className, render }: Props = $props();
|
||||||
|
|
||||||
const baseClasses = 'font-mono text-[0.5625rem] sm:text-[0.625rem] uppercase tracking-[0.2em] text-gray-500 opacity-60';
|
const baseClasses =
|
||||||
|
'font-mono text-[0.5625rem] sm:text-[0.625rem] uppercase tracking-[0.2em] text-text-soft opacity-60';
|
||||||
const combinedClasses = cn(baseClasses, className);
|
const combinedClasses = cn(baseClasses, className);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ let { rotation = 'clockwise', icon, ...rest }: Props = $props();
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="
|
class="
|
||||||
group relative border-none size-9
|
group relative border-none size-9
|
||||||
bg-white/20 hover:bg-white/60
|
bg-background-20 hover:bg-background-60
|
||||||
backdrop-blur-3xl
|
backdrop-blur-3xl
|
||||||
transition-all duration-200 ease-out
|
transition-all duration-200 ease-out
|
||||||
will-change-transform
|
will-change-transform
|
||||||
@@ -43,7 +43,7 @@ let { rotation = 'clockwise', icon, ...rest }: Props = $props();
|
|||||||
>
|
>
|
||||||
{@render icon({
|
{@render icon({
|
||||||
className: cn(
|
className: cn(
|
||||||
'size-4 transition-all duration-200 stroke-[1.5] stroke-gray-500 group-hover:stroke-gray-900 group-hover:scale-110 group-hover:stroke-3 group-active:scale-90 group-disabled:stroke-transparent',
|
'size-4 transition-all duration-200 stroke-[1.5] stroke-text-muted group-hover:stroke-foreground group-hover:scale-110 group-hover:stroke-3 group-active:scale-90 group-disabled:stroke-transparent',
|
||||||
rotation === 'clockwise' ? 'group-active:rotate-6' : 'group-active:-rotate-6',
|
rotation === 'clockwise' ? 'group-active:rotate-6' : 'group-active:-rotate-6',
|
||||||
),
|
),
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -35,19 +35,19 @@ const isGhost = $derived(variant === 'ghost');
|
|||||||
class={cn(
|
class={cn(
|
||||||
'h-12 sm:h-14 md:h-16 w-full text-sm sm:text-base',
|
'h-12 sm:h-14 md:h-16 w-full text-sm sm:text-base',
|
||||||
'backdrop-blur-md',
|
'backdrop-blur-md',
|
||||||
isGhost ? 'bg-transparent' : 'bg-white/80',
|
isGhost ? 'bg-transparent' : 'bg-background-80',
|
||||||
'border border-gray-300/50',
|
'border border-border-muted',
|
||||||
isGhost ? 'border-transparent' : 'border-gray-300/50',
|
isGhost ? 'border-transparent' : 'border-border-muted',
|
||||||
isGhost ? 'shadow-none' : 'shadow-[0_1px_3px_rgba(0,0,0,0.04)]',
|
isGhost ? 'shadow-none' : 'shadow-[0_1px_3px_rgba(0,0,0,0.04)]',
|
||||||
'focus-visible:border-gray-400/60',
|
'focus-visible:border-border-soft',
|
||||||
'focus-visible:outline-none',
|
'focus-visible:outline-none',
|
||||||
'focus-visible:ring-1',
|
'focus-visible:ring-1',
|
||||||
'focus-visible:ring-gray-400/30',
|
'focus-visible:ring-border-muted/30',
|
||||||
'focus-visible:bg-white/90',
|
'focus-visible:bg-background-95',
|
||||||
'hover:bg-white/90',
|
'hover:bg-background-95',
|
||||||
'hover:border-gray-400/60',
|
'hover:border-border-soft',
|
||||||
'text-gray-900',
|
'text-foreground',
|
||||||
'placeholder:text-gray-400',
|
'placeholder:text-text-muted',
|
||||||
'placeholder:font-mono',
|
'placeholder:font-mono',
|
||||||
'placeholder:text-xs sm:placeholder:text-sm',
|
'placeholder:text-xs sm:placeholder:text-sm',
|
||||||
'placeholder:tracking-wide',
|
'placeholder:tracking-wide',
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ let { size = 20, class: className = '', message = 'analyzing_data' }: Props = $p
|
|||||||
out:fade={{ duration: 300 }}
|
out:fade={{ duration: 300 }}
|
||||||
>
|
>
|
||||||
<div style:width="{size}px" style:height="{size}px">
|
<div style:width="{size}px" style:height="{size}px">
|
||||||
<svg class="stroke-gray-900 stroke-1" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg class="stroke-foreground stroke-1" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g transform="translate(12, 12)">
|
<g transform="translate(12, 12)">
|
||||||
<!-- Four corner brackets rotating -->
|
<!-- Four corner brackets rotating -->
|
||||||
<g>
|
<g>
|
||||||
@@ -68,10 +68,10 @@ let { size = 20, class: className = '', message = 'analyzing_data' }: Props = $p
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<!-- Divider -->
|
<!-- Divider -->
|
||||||
<div class="w-px h-3 bg-gray-400/50"></div>
|
<div class="w-px h-3 bg-text-muted/50"></div>
|
||||||
|
|
||||||
<!-- Message -->
|
<!-- Message -->
|
||||||
<span class="font-mono text-[10px] uppercase tracking-[0.2em] text-gray-600 font-medium">
|
<span class="font-mono text-[10px] uppercase tracking-[0.2em] text-text-subtle font-medium">
|
||||||
{message}
|
{message}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ import {
|
|||||||
import { Footnote } from '..';
|
import { Footnote } from '..';
|
||||||
|
|
||||||
interface Props extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
interface Props extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
||||||
|
/**
|
||||||
|
* ID of the section
|
||||||
|
*/
|
||||||
|
id?: string;
|
||||||
/**
|
/**
|
||||||
* Additional CSS classes to apply to the section container.
|
* Additional CSS classes to apply to the section container.
|
||||||
*/
|
*/
|
||||||
@@ -40,16 +44,22 @@ interface Props extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
|||||||
* @param index - Index of the section
|
* @param index - Index of the section
|
||||||
* @param isPast - Whether the section is past the current scroll position
|
* @param isPast - Whether the section is past the current scroll position
|
||||||
* @param title - Snippet for a title itself
|
* @param title - Snippet for a title itself
|
||||||
|
* @param id - ID of the section
|
||||||
* @returns Cleanup callback
|
* @returns Cleanup callback
|
||||||
*/
|
*/
|
||||||
onTitleStatusChange?: (index: number, isPast: boolean, title?: Snippet<[{ className?: string }]>) => () => void;
|
onTitleStatusChange?: (
|
||||||
|
index: number,
|
||||||
|
isPast: boolean,
|
||||||
|
title?: Snippet<[{ className?: string }]>,
|
||||||
|
id?: string,
|
||||||
|
) => () => void;
|
||||||
/**
|
/**
|
||||||
* Snippet for the section content
|
* Snippet for the section content
|
||||||
*/
|
*/
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { class: className, title, icon, description, index = 0, onTitleStatusChange, children }: Props = $props();
|
const { class: className, title, icon, description, index = 0, onTitleStatusChange, id, children }: Props = $props();
|
||||||
|
|
||||||
let titleContainer = $state<HTMLElement>();
|
let titleContainer = $state<HTMLElement>();
|
||||||
const flyParams: FlyParams = { y: 0, x: -50, duration: 300, easing: cubicOut, opacity: 0.2 };
|
const flyParams: FlyParams = { y: 0, x: -50, duration: 300, easing: cubicOut, opacity: 0.2 };
|
||||||
@@ -68,7 +78,7 @@ $effect(() => {
|
|||||||
|
|
||||||
if (isPast !== isScrolledPast) {
|
if (isPast !== isScrolledPast) {
|
||||||
isScrolledPast = isPast;
|
isScrolledPast = isPast;
|
||||||
cleanup = onTitleStatusChange?.(index, isPast, title);
|
cleanup = onTitleStatusChange?.(index, isPast, title, id);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
// Set threshold to 0 to trigger exactly when the last pixel leaves
|
// Set threshold to 0 to trigger exactly when the last pixel leaves
|
||||||
@@ -84,6 +94,7 @@ $effect(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
|
id={id}
|
||||||
class={cn(
|
class={cn(
|
||||||
'flex flex-col',
|
'flex flex-col',
|
||||||
className,
|
className,
|
||||||
@@ -94,8 +105,8 @@ $effect(() => {
|
|||||||
<div class="flex flex-col gap-2 sm:gap-3" bind:this={titleContainer}>
|
<div class="flex flex-col gap-2 sm:gap-3" bind:this={titleContainer}>
|
||||||
<div class="flex items-center gap-2 sm:gap-3">
|
<div class="flex items-center gap-2 sm:gap-3">
|
||||||
{#if icon}
|
{#if icon}
|
||||||
{@render icon({ className: 'size-3 sm:size-4 stroke-gray-900 stroke-1 opacity-60' })}
|
{@render icon({ className: 'size-3 sm:size-4 stroke-foreground stroke-1 opacity-60' })}
|
||||||
<div class="w-px h-2.5 sm:h-3 bg-gray-300/60"></div>
|
<div class="w-px h-2.5 sm:h-3 bg-border-subtle"></div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if description}
|
{#if description}
|
||||||
<Footnote>
|
<Footnote>
|
||||||
@@ -113,7 +124,7 @@ $effect(() => {
|
|||||||
{#if title}
|
{#if title}
|
||||||
{@render title({
|
{@render title({
|
||||||
className:
|
className:
|
||||||
'text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-semibold tracking-tighter text-gray-900 leading-[0.9]',
|
'text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-semibold tracking-tighter text-foreground leading-[0.9]',
|
||||||
})}
|
})}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const { Story } = defineMeta({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="flex flex-col gap-4 p-4 w-full">
|
<div class="flex flex-col gap-4 p-4 w-full">
|
||||||
<div class="flex flex-col gap-2 p-4 border rounded-xl border-gray-200/50 bg-white/40">
|
<div class="flex flex-col gap-2 p-4 border rounded-xl border-border-subtle bg-background-40">
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<Skeleton class="h-8 w-1/3" />
|
<Skeleton class="h-8 w-1/3" />
|
||||||
<Skeleton class="h-8 w-8 rounded-full" />
|
<Skeleton class="h-8 w-8 rounded-full" />
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ let { class: className, animate = true, ...rest }: Props = $props();
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
class={cn(
|
class={cn(
|
||||||
'rounded-md bg-gray-100/50 backdrop-blur-sm',
|
'rounded-md bg-background-subtle/50 backdrop-blur-sm',
|
||||||
animate && 'animate-pulse',
|
animate && 'animate-pulse',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -43,11 +43,11 @@ let { value = $bindable(), orientation = 'horizontal', class: className, ...rest
|
|||||||
{#snippet children(props)}
|
{#snippet children(props)}
|
||||||
<span
|
<span
|
||||||
{...props}
|
{...props}
|
||||||
class={cn('relative bg-gray-200 rounded-full', orientation === 'horizontal' ? 'w-full h-px' : 'h-full w-px')}
|
class={cn('relative bg-background-muted rounded-full', orientation === 'horizontal' ? 'w-full h-px' : 'h-full w-px')}
|
||||||
>
|
>
|
||||||
<!-- Filled range with NO transition -->
|
<!-- Filled range with NO transition -->
|
||||||
<Slider.Range
|
<Slider.Range
|
||||||
class={cn('absolute bg-gray-900 rounded-full', orientation === 'horizontal' ? 'h-full' : 'w-full')}
|
class={cn('absolute bg-foreground rounded-full', orientation === 'horizontal' ? 'h-full' : 'w-full')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Slider.Thumb
|
<Slider.Thumb
|
||||||
@@ -56,7 +56,7 @@ let { value = $bindable(), orientation = 'horizontal', class: className, ...rest
|
|||||||
'group/thumb relative block',
|
'group/thumb relative block',
|
||||||
orientation === 'horizontal' ? '-top-1 w-2 h-2.25' : '-left-1 h-2 w-2.25',
|
orientation === 'horizontal' ? '-top-1 w-2 h-2.25' : '-left-1 h-2 w-2.25',
|
||||||
'rounded-sm',
|
'rounded-sm',
|
||||||
'bg-gray-900',
|
'bg-foreground',
|
||||||
// Glow shadow
|
// Glow shadow
|
||||||
'shadow-[0_0_6px_rgba(0,0,0,0.4)]',
|
'shadow-[0_0_6px_rgba(0,0,0,0.4)]',
|
||||||
// Smooth transitions only for size/position
|
// Smooth transitions only for size/position
|
||||||
@@ -76,7 +76,7 @@ let { value = $bindable(), orientation = 'horizontal', class: className, ...rest
|
|||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
absolute inset-0 rounded-sm
|
absolute inset-0 rounded-sm
|
||||||
bg-white/20
|
bg-background-20
|
||||||
opacity-0 group-hover/thumb:opacity-100
|
opacity-0 group-hover/thumb:opacity-100
|
||||||
transition-opacity duration-200
|
transition-opacity duration-200
|
||||||
"
|
"
|
||||||
@@ -89,8 +89,8 @@ let { value = $bindable(), orientation = 'horizontal', class: className, ...rest
|
|||||||
'absolute',
|
'absolute',
|
||||||
orientation === 'horizontal' ? '-top-8 left-1/2 -translate-x-1/2' : 'left-5 top-1/2 -translate-y-1/2',
|
orientation === 'horizontal' ? '-top-8 left-1/2 -translate-x-1/2' : 'left-5 top-1/2 -translate-y-1/2',
|
||||||
'px-1.5 py-0.5 rounded-md',
|
'px-1.5 py-0.5 rounded-md',
|
||||||
'bg-gray-900/90 backdrop-blur-sm',
|
'bg-foreground/90 backdrop-blur-sm',
|
||||||
'font-mono text-[0.625rem] font-medium text-white ',
|
'font-mono text-[0.625rem] font-medium text-background',
|
||||||
'opacity-0 group-hover/thumb:opacity-100',
|
'opacity-0 group-hover/thumb:opacity-100',
|
||||||
'transition-all duration-300',
|
'transition-all duration-300',
|
||||||
'pointer-events-none',
|
'pointer-events-none',
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ $effect(() => {
|
|||||||
rounded-xl sm:rounded-2xl md:rounded-[2.5rem]
|
rounded-xl sm:rounded-2xl md:rounded-[2.5rem]
|
||||||
select-none touch-none cursor-ew-resize min-h-72 sm:min-h-96 flex flex-col justify-center
|
select-none touch-none cursor-ew-resize min-h-72 sm:min-h-96 flex flex-col justify-center
|
||||||
backdrop-blur-lg bg-linear-to-br from-gray-100/70 via-white/50 to-gray-100/60
|
backdrop-blur-lg bg-linear-to-br from-gray-100/70 via-white/50 to-gray-100/60
|
||||||
border border-gray-300/40
|
border border-border-muted
|
||||||
shadow-[inset_0_4px_12px_0_rgba(0,0,0,0.12),inset_0_2px_4px_0_rgba(0,0,0,0.08),0_1px_2px_0_rgba(255,255,255,0.8)]
|
shadow-[inset_0_4px_12px_0_rgba(0,0,0,0.12),inset_0_2px_4px_0_rgba(0,0,0,0.08),0_1px_2px_0_rgba(255,255,255,0.8)]
|
||||||
before:absolute before:inset-0 before:rounded-xl sm:before:rounded-2xl md:before:rounded-[2.5rem] before:p-px
|
before:absolute before:inset-0 before:rounded-xl sm:before:rounded-2xl md:before:rounded-[2.5rem] before:p-px
|
||||||
before:bg-linear-to-br before:from-black/5 before:via-black/2 before:to-transparent
|
before:bg-linear-to-br before:from-black/5 before:via-black/2 before:to-transparent
|
||||||
|
|||||||
@@ -63,10 +63,10 @@ function selectFontB(font: UnifiedFont) {
|
|||||||
<SelectRoot type="single" disabled={!fontList.length}>
|
<SelectRoot type="single" disabled={!fontList.length}>
|
||||||
<SelectTrigger
|
<SelectTrigger
|
||||||
class={cn(
|
class={cn(
|
||||||
'w-36 sm:w-44 md:w-52 h-8 sm:h-9 border border-gray-300/40 bg-white/60 backdrop-blur-sm',
|
'w-36 sm:w-44 md:w-52 h-8 sm:h-9 border border-border-muted bg-background-60 backdrop-blur-sm',
|
||||||
'px-2 sm:px-3 rounded-lg transition-all flex items-center justify-between gap-2',
|
'px-2 sm:px-3 rounded-lg transition-all flex items-center justify-between gap-2',
|
||||||
'font-mono text-[10px] sm:text-[11px] tracking-tight font-medium text-gray-900',
|
'font-mono text-[10px] sm:text-[11px] tracking-tight font-medium text-foreground',
|
||||||
'hover:bg-white/80 hover:border-gray-400/60 hover:shadow-sm',
|
'hover:bg-background-80 hover:border-border-soft hover:shadow-sm',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div class="text-left flex-1 min-w-0">
|
<div class="text-left flex-1 min-w-0">
|
||||||
@@ -77,7 +77,7 @@ function selectFontB(font: UnifiedFont) {
|
|||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent
|
<SelectContent
|
||||||
class={cn(
|
class={cn(
|
||||||
'bg-white/95 backdrop-blur-xl border border-gray-300/50 shadow-xl',
|
'bg-background-95 backdrop-blur-xl border border-border-muted shadow-xl',
|
||||||
'w-44 sm:w-52 max-h-60 sm:max-h-64 overflow-hidden rounded-lg',
|
'w-44 sm:w-52 max-h-60 sm:max-h-64 overflow-hidden rounded-lg',
|
||||||
)}
|
)}
|
||||||
side="top"
|
side="top"
|
||||||
@@ -91,7 +91,7 @@ function selectFontB(font: UnifiedFont) {
|
|||||||
{@const handleClick = () => onSelect(fontListItem)}
|
{@const handleClick = () => onSelect(fontListItem)}
|
||||||
<SelectItem
|
<SelectItem
|
||||||
value={fontListItem.id}
|
value={fontListItem.id}
|
||||||
class="data-highlighted:bg-gray-100 font-mono text-[10px] sm:text-[11px] px-2 sm:px-3 py-2 sm:py-2.5 rounded-md cursor-pointer transition-colors"
|
class="data-highlighted:bg-background-muted font-mono text-[10px] sm:text-[11px] px-2 sm:px-3 py-2 sm:py-2.5 rounded-md cursor-pointer transition-colors"
|
||||||
onclick={handleClick}
|
onclick={handleClick}
|
||||||
>
|
>
|
||||||
<FontApplicator
|
<FontApplicator
|
||||||
@@ -117,8 +117,8 @@ function selectFontB(font: UnifiedFont) {
|
|||||||
>
|
>
|
||||||
<div class="flex items-center gap-2 sm:gap-2.5 px-1">
|
<div class="flex items-center gap-2 sm:gap-2.5 px-1">
|
||||||
<div class="w-1.5 h-1.5 rounded-full bg-indigo-500 shadow-[0_0_6px_rgba(99,102,241,0.6)]"></div>
|
<div class="w-1.5 h-1.5 rounded-full bg-indigo-500 shadow-[0_0_6px_rgba(99,102,241,0.6)]"></div>
|
||||||
<div class="w-px h-2 sm:h-2.5 bg-gray-300/60"></div>
|
<div class="w-px h-2 sm:h-2.5 bg-border-subtle"></div>
|
||||||
<span class="font-mono text-[8px] sm:text-[9px] uppercase tracking-[0.2em] text-gray-500 font-medium">
|
<span class="font-mono text-[8px] sm:text-[9px] uppercase tracking-[0.2em] text-text-subtle font-medium">
|
||||||
ch_01
|
ch_01
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -133,11 +133,11 @@ function selectFontB(font: UnifiedFont) {
|
|||||||
style:transform="translateY({sliderPos > 80 ? '8px' : '0px'})"
|
style:transform="translateY({sliderPos > 80 ? '8px' : '0px'})"
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-2 sm:gap-2.5 px-1">
|
<div class="flex items-center gap-2 sm:gap-2.5 px-1">
|
||||||
<span class="font-mono text-[8px] sm:text-[9px] uppercase tracking-[0.2em] text-gray-500 font-medium">
|
<span class="font-mono text-[8px] sm:text-[9px] uppercase tracking-[0.2em] text-text-subtle font-medium">
|
||||||
ch_02
|
ch_02
|
||||||
</span>
|
</span>
|
||||||
<div class="w-px h-2 sm:h-2.5 bg-gray-300/60"></div>
|
<div class="w-px h-2 sm:h-2.5 bg-border-subtle"></div>
|
||||||
<div class="w-1.5 h-1.5 rounded-full bg-gray-900 shadow-[0_0_6px_rgba(0,0,0,0.4)]"></div>
|
<div class="w-1.5 h-1.5 rounded-full bg-foreground shadow-[0_0_6px_rgba(0,0,0,0.4)]"></div>
|
||||||
</div>
|
</div>
|
||||||
{#if fontA && fontAUrl}
|
{#if fontA && fontAUrl}
|
||||||
{@render fontSelector(fontA, fontList, fontAUrl, selectFontA, 'end')}
|
{@render fontSelector(fontA, fontList, fontAUrl, selectFontA, 'end')}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ let { sliderPos, isDragging }: Props = $props();
|
|||||||
<div
|
<div
|
||||||
class={cn(
|
class={cn(
|
||||||
'relative h-full rounded-sm transition-all duration-300',
|
'relative h-full rounded-sm transition-all duration-300',
|
||||||
'bg-white/3 ',
|
'bg-background-3 ',
|
||||||
// These are the visible "edges" of the glass
|
// These are the visible "edges" of the glass
|
||||||
'shadow-[0_0_40px_rgba(0,0,0,0.1)_inset_0_0_20px_rgba(255,255,255,0.1)]',
|
'shadow-[0_0_40px_rgba(0,0,0,0.1)_inset_0_0_20px_rgba(255,255,255,0.1)]',
|
||||||
'shadow-[0_10px_30px_-10px_rgba(0,0,0,0.2),inset_0_1px_1px_rgba(255,255,255,0.4)]',
|
'shadow-[0_10px_30px_-10px_rgba(0,0,0,0.2),inset_0_1px_1px_rgba(255,255,255,0.4)]',
|
||||||
|
|||||||
@@ -77,14 +77,14 @@ function toggleFilters() {
|
|||||||
|
|
||||||
<div class="absolute right-4 top-1/2 translate-y-[-50%] z-10">
|
<div class="absolute right-4 top-1/2 translate-y-[-50%] z-10">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<div class="w-px h-5 bg-gray-300/60"></div>
|
<div class="w-px h-5 bg-border-subtle"></div>
|
||||||
<div style:transform="scale({transform.current.scale})">
|
<div style:transform="scale({transform.current.scale})">
|
||||||
<IconButton onclick={toggleFilters}>
|
<IconButton onclick={toggleFilters}>
|
||||||
{#snippet icon({ className })}
|
{#snippet icon({ className })}
|
||||||
<SlidersHorizontalIcon
|
<SlidersHorizontalIcon
|
||||||
class={cn(
|
class={cn(
|
||||||
className,
|
className,
|
||||||
showFilters ? 'stroke-gray-900 stroke-3' : 'stroke-gray-500',
|
showFilters ? 'stroke-foreground stroke-3' : 'stroke-text-muted',
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@@ -102,14 +102,14 @@ function toggleFilters() {
|
|||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
p-3 sm:p-4 md:p-5 rounded-xl
|
p-3 sm:p-4 md:p-5 rounded-xl
|
||||||
backdrop-blur-md bg-white/80
|
backdrop-blur-md bg-background-80
|
||||||
border border-gray-300/50
|
border border-border-muted
|
||||||
shadow-[0_1px_3px_rgba(0,0,0,0.04)]
|
shadow-[0_1px_3px_rgba(0,0,0,0.04)]
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-2 sm:gap-2.5 mb-3 sm:mb-4">
|
<div class="flex items-center gap-2 sm:gap-2.5 mb-3 sm:mb-4">
|
||||||
<div class="w-1 h-1 rounded-full bg-gray-900 opacity-70"></div>
|
<div class="w-1 h-1 rounded-full bg-foreground opacity-70"></div>
|
||||||
<div class="w-px h-2.5 bg-gray-300/60"></div>
|
<div class="w-px h-2.5 bg-border-subtle"></div>
|
||||||
<Footnote>
|
<Footnote>
|
||||||
filter_params
|
filter_params
|
||||||
</Footnote>
|
</Footnote>
|
||||||
@@ -119,7 +119,7 @@ function toggleFilters() {
|
|||||||
<Filters />
|
<Filters />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-3 sm:mt-4 pt-3 sm:pt-4 border-t border-gray-300/40">
|
<div class="mt-3 sm:mt-4 pt-3 sm:pt-4 border-t border-border-subtle">
|
||||||
<FilterControls class="m-auto w-fit" />
|
<FilterControls class="m-auto w-fit" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user