Skip to content

Commit 3f62d45

Browse files
authored
Update shadcn theme and fix/standardize styling (#2193)
* Update to latest shadcn themes * Fix broken theme values The contrast of the current values is unacceptable and the themes demo page is using these new values * Improve theme-picker trigger contrast on home page * Adapt ListItem to new theme colors * Fix: dark outline button border color * Adapt ghost and shell inputs to new shadcn theme * Use button styling for sort badge-button * Redesign ListItem states Now it uses "focus-visible:ring-[3px] focus-visible:ring-ring/50" like everything else * Fix broken outlines in dark mode This was only affecting ListItem, which isn't using an outline anymore. So, the issue doesn't currently affect us, but it was VERY confusing. * Improve writing system contrast * Fix Add sense button outline cropped * Improve primary color contrast * Add story for demoing theme colors
1 parent 39a7877 commit 3f62d45

19 files changed

Lines changed: 781 additions & 396 deletions

frontend/viewer/src/app.css

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
@utility animate-shimmer {
1717
animation: shimmer 2s linear infinite;
18-
background-image: linear-gradient(90deg, hsl(var(--muted-foreground)/0.2) 20%, hsl(var(--foreground)/0.3) 50%, hsl(var(--muted-foreground)/0.2) 65%);
18+
background-image: linear-gradient(90deg, oklch(from var(--muted-foreground) l c h / 0.2) 20%, oklch(from var(--foreground) l c h / 0.3) 50%, oklch(from var(--muted-foreground) l c h / 0.2) 65%);
1919
background-size: 200% 100%;
2020
background-repeat: repeat;
2121
}
@@ -35,7 +35,9 @@
3535
@layer base {
3636
/* shadcn generated styles */
3737
* {
38-
@apply border-border;
38+
@apply border-border outline-ring/50;
39+
/* EDIT: Fixes dark mode gets "outline-style: none" which results in white (browser bug?) */
40+
@apply focus-visible:outline-solid;
3941
}
4042
.app {
4143
@apply bg-background text-foreground;

frontend/viewer/src/home/AppBar.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
linear-gradient is just a way to use a solid color and work around: "Only the last background can include a background color."
3030
See: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_backgrounds_and_borders/Using_multiple_backgrounds
3131
*/
32-
linear-gradient(hsl(var(--primary) / 0.4)),
33-
hsl(var(--background));
32+
linear-gradient(oklch(from var(--primary) l c h / 0.4)),
33+
var(--background);
3434
}
3535
</style>

frontend/viewer/src/home/HomeView.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@
138138
<Button href="/swagger" variant="ghost" size="icon" icon="i-mdi-api" target="_blank" title="Swagger"/>
139139
</DevContent>
140140
<LocalizationPicker/>
141-
<ThemePicker buttonProps={{variant: 'outline'}}/>
141+
<ThemePicker />
142142
<ResponsiveMenu.Root>
143143
<ResponsiveMenu.Trigger/>
144144
<ResponsiveMenu.Content>

frontend/viewer/src/home/ProjectListItem.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
{#if skeleton || !project}
2222
<ListItem {...rest}
23-
class="animate-pulse dark:bg-muted/50 bg-muted/80 hover:bg-muted/30 hover:dark:bg-muted dark:text-neutral-50/50 cursor-default text-neutral-500">
23+
class="animate-pulse hover:bg-muted/30 hover:dark:bg-muted dark:text-neutral-50/50 cursor-default text-neutral-500">
2424
<div class="h-4 dark:bg-neutral-50/50 bg-neutral-500 rounded-full w-32"></div>
2525
<div class="h-3 mt-3 dark:bg-neutral-50/50 bg-neutral-500 rounded-full w-20"></div>
2626
</ListItem>

frontend/viewer/src/lib/components/ListItem.svelte

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@
3737
data-skeleton={skeleton || undefined}
3838
class={cn(
3939
'w-full max-w-full px-4 py-3 flex text-left overflow-hidden items-center gap-4',
40-
'dark:bg-muted/50 bg-muted/80 hover:bg-primary/15 hover:dark:bg-primary/15 aria-selected:ring-2 ring-primary ring-offset-background rounded',
41-
'shadow hover:shadow-lg hover:z-10',
40+
'bg-muted rounded outline-none shadow-sm hover:shadow-md hover:z-10',
41+
'focus-visible:ring-[3px] focus-visible:ring-ring/50',
42+
'border-l-5 border-l-transparent aria-selected:border-l-primary',
43+
'hover:bg-primary/15 aria-selected:bg-primary/15',
44+
'dark:hover:bg-primary/25 aria-selected:dark:bg-primary/25',
4245
'disabled:pointer-events-none disabled:contrast-[0.8]',
4346
loading && 'animate-pulse',
4447
skeleton && 'cursor-default hover:bg-transparent pointer-events-none shadow-none',

frontend/viewer/src/lib/components/ThemePicker.svelte

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,38 @@
1+
<script module lang="ts">
2+
import {msg} from 'svelte-i18n-lingui';
3+
4+
export const themes = [msg`green`, msg`blue`, msg`rose`, msg`orange`, msg`violet`, msg`stone`];
5+
</script>
6+
17
<script lang="ts">
28
import {Button, type ButtonProps, XButton} from '$lib/components/ui/button';
39
import {Icon} from '$lib/components/ui/icon';
410
import {Label} from '$lib/components/ui/label/index.js';
511
import * as Popover from '$lib/components/ui/popover';
612
import {cn} from '$lib/utils';
713
import {mode, resetMode, setMode, setTheme, theme, userPrefersMode} from 'mode-watcher';
8-
import {t, msg} from 'svelte-i18n-lingui';
9-
import {mergeProps} from 'bits-ui';
14+
import {t} from 'svelte-i18n-lingui';
1015
11-
const themes = [msg`green`, msg`blue`, msg`rose`, msg`orange`, msg`violet`, msg`stone`];
1216
let {
13-
buttonProps = {},
17+
variant,
1418
}: {
15-
buttonProps?: Partial<ButtonProps>;
19+
variant?: ButtonProps['variant'] & ('default' | 'ghost');
1620
} = $props();
21+
22+
const triggerClass = $derived(variant === 'ghost' ? 'text-primary' : undefined);
1723
</script>
1824

1925
<Popover.Root>
2026
<Popover.Trigger>
2127
{#snippet child({props})}
22-
<Button variant="ghost" size="icon" {...mergeProps(props, buttonProps)}>
28+
<Button {variant} size="icon" {...props}>
2329
<Icon
2430
icon="i-mdi-white-balance-sunny"
25-
class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0 text-primary"
31+
class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0 {triggerClass}"
2632
/>
2733
<Icon
2834
icon="i-mdi-weather-night"
29-
class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100 text-primary"
35+
class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100 {triggerClass}"
3036
/>
3137
<span class="sr-only">{$t`Choose theme`}</span>
3238
</Button>

frontend/viewer/src/lib/components/ui/button/button.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99
import Anchor from '../anchor/anchor.svelte';
1010
1111
export const buttonVariants = tv({
12-
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:[&:not(.loading)]:opacity-50 aria-disabled:pointer-events-none aria-disabled:[&:not(.loading)]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
12+
base: "focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:[&:not(.loading)]:opacity-50 aria-disabled:pointer-events-none aria-disabled:[&:not(.loading)]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1313
variants: {
1414
variant: {
1515
default: 'bg-primary text-primary-foreground hover:bg-primary/90 shadow-xs',
1616
destructive:
1717
'bg-destructive hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white shadow-xs',
18+
/* all border classes have been moved here, because they were/are the only variant that actually uses them */
1819
outline:
19-
'bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border shadow-xs',
20+
'bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 focus-visible:border-ring border border-input aria-invalid:border-destructive dark:hover:bg-input/50 shadow-xs',
2021
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-xs',
2122
ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
2223
link: 'text-primary underline-offset-4 hover:underline',

frontend/viewer/src/lib/components/ui/input/composable-input.svelte

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,9 @@
3232
3333
const stringPlaceholder = $derived(typeof placeholder === 'string' ? placeholder : undefined);
3434
const snippetPlaceholder = $derived(typeof placeholder === 'function' ? placeholder : undefined);
35-
const focusRingClass =
36-
'has-[.real-input:focus-visible]:ring-ring has-[.real-input:focus-visible]:outline-none has-[.real-input:focus-visible]:ring-2 has-[.real-input:focus-visible]:ring-offset-2';
3735
</script>
3836

39-
<InputShell bind:ref {focusRingClass} class={cn('gap-0', className)} {...restProps}>
37+
<InputShell bind:ref class={cn('gap-0', className)} {...restProps}>
4038
{@render before?.()}
4139
<div class="grow flex relative overflow-hidden items-center h-full">
4240
<Input

frontend/viewer/src/lib/components/ui/input/input-shell.svelte

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,14 @@
44
import type {HTMLAttributes} from 'svelte/elements';
55
import {inputVariants} from './input.svelte';
66
7-
interface Props extends WithElementRef<HTMLAttributes<HTMLDivElement>> {
8-
focusRingClass?: string;
9-
}
10-
11-
const anyChildHasFocusRing = 'ring-ring outline-none ring-offset-2 has-focus-visible:ring-2';
12-
137
let {
148
ref = $bindable(null),
159
class: className,
16-
focusRingClass = anyChildHasFocusRing,
1710
children,
1811
...restProps
19-
}: Props = $props();
12+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
2013
</script>
2114

22-
<div bind:this={ref} class={cn(inputVariants({variant: 'shell'}), focusRingClass, className)} {...restProps}>
15+
<div bind:this={ref} class={cn(inputVariants({variant: 'shell'}), className)} {...restProps}>
2316
{@render children?.()}
2417
</div>

frontend/viewer/src/lib/components/ui/input/input.svelte

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@
1717
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
1818
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
1919
),
20-
ghost: 'outline-none min-w-0 bg-transparent',
21-
// shell variant has a dedicated component
22-
shell:
23-
'border-input bg-background ring-offset-background placeholder:text-muted-foreground flex h-10 w-full rounded-md border text-base has-disabled:cursor-not-allowed has-disabled:opacity-50 md:text-sm flex gap-2 items-center justify-between',
20+
ghost: 'outline-none min-w-0 bg-transparent selection:bg-primary selection:text-primary-foreground',
21+
// shell variant has a dedicated component. Users should apply the class "real-input" to a wrapped <input> to enable focus styles on the shell.
22+
shell: cn(
23+
'border-input bg-background selection:bg-primary dark:bg-input/30 selection:text-primary-foreground ring-offset-background placeholder:text-muted-foreground flex h-10 w-full min-w-0 rounded-md border text-base shadow-xs transition-[color,box-shadow] outline-none has-disabled:cursor-not-allowed has-disabled:opacity-50 md:text-sm',
24+
'flex gap-2 items-center justify-between',
25+
'has-[.real-input:focus-visible]:border-ring has-[.real-input:focus-visible]:ring-ring/50 has-[.real-input:focus-visible]:ring-[3px]',
26+
'has-[.real-input:aria-invalid]:ring-destructive/20 dark:has-[.real-input:aria-invalid]:ring-destructive/40 has-[.real-input:aria-invalid]:border-destructive',
27+
),
2428
},
2529
visibleFocus: {
2630
off: '',

0 commit comments

Comments
 (0)