refactor: tailwind tier 1 — border-subtle/text-secondary/focus-ring utilities + Input config extraction
This commit is contained in:
@@ -111,7 +111,7 @@ const variantStyles: Record<ButtonVariant, string> = {
|
||||
),
|
||||
ghost: cn(
|
||||
'bg-transparent',
|
||||
'text-neutral-500 dark:text-neutral-400',
|
||||
'text-secondary',
|
||||
'border border-transparent',
|
||||
'hover:bg-transparent dark:hover:bg-transparent',
|
||||
'hover:text-brand dark:hover:text-brand',
|
||||
@@ -121,7 +121,7 @@ const variantStyles: Record<ButtonVariant, string> = {
|
||||
),
|
||||
icon: cn(
|
||||
'bg-surface dark:bg-dark-bg',
|
||||
'text-neutral-500 dark:text-neutral-400',
|
||||
'text-secondary',
|
||||
'border border-transparent',
|
||||
'hover:bg-paper dark:hover:bg-paper',
|
||||
'hover:text-brand',
|
||||
@@ -172,7 +172,7 @@ const activeStyles: Partial<Record<ButtonVariant, string>> = {
|
||||
'bg-paper dark:bg-dark-card border-black/10 dark:border-white/10 shadow-sm text-neutral-900 dark:text-neutral-100',
|
||||
ghost: 'bg-transparent dark:bg-transparent text-brand dark:text-brand',
|
||||
outline: 'bg-surface dark:bg-paper border-brand',
|
||||
icon: 'bg-paper dark:bg-paper text-brand border-black/5 dark:border-white/10',
|
||||
icon: 'bg-paper dark:bg-paper text-brand border-subtle',
|
||||
};
|
||||
|
||||
const classes = $derived(cn(
|
||||
@@ -184,7 +184,7 @@ const classes = $derived(cn(
|
||||
'select-none',
|
||||
'outline-none',
|
||||
'cursor-pointer',
|
||||
'focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2',
|
||||
'focus-ring',
|
||||
'focus-visible:ring-offset-surface dark:focus-visible:ring-offset-dark-bg',
|
||||
'disabled:cursor-not-allowed disabled:pointer-events-none',
|
||||
// Variant
|
||||
|
||||
@@ -26,7 +26,7 @@ let { children, class: className, ...rest }: Props = $props();
|
||||
class={cn(
|
||||
'flex items-center gap-1 p-1',
|
||||
'bg-surface dark:bg-dark-bg',
|
||||
'border border-black/5 dark:border-white/10',
|
||||
'border border-subtle',
|
||||
'rounded-none',
|
||||
'transition-colors duration-500',
|
||||
className,
|
||||
|
||||
@@ -93,9 +93,7 @@ const displayLabel = $derived(label ?? controlLabel ?? '');
|
||||
step={control.step}
|
||||
orientation="horizontal"
|
||||
/>
|
||||
<span
|
||||
class="font-mono text-[0.6875rem] text-neutral-500 dark:text-neutral-400 tabular-nums w-10 text-right shrink-0"
|
||||
>
|
||||
<span class="font-mono text-[0.6875rem] text-secondary tabular-nums w-10 text-right shrink-0">
|
||||
{formattedValue()}
|
||||
</span>
|
||||
</div>
|
||||
@@ -129,7 +127,7 @@ const displayLabel = $derived(label ?? controlLabel ?? '');
|
||||
'border border-transparent',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand/30',
|
||||
open
|
||||
? 'bg-paper dark:bg-dark-card shadow-sm border-black/5 dark:border-white/10'
|
||||
? 'bg-paper dark:bg-dark-card shadow-sm border-subtle'
|
||||
: 'hover:bg-paper/50 dark:hover:bg-dark-card/50',
|
||||
)}
|
||||
aria-label={controlLabel}
|
||||
@@ -157,7 +155,7 @@ const displayLabel = $derived(label ?? controlLabel ?? '');
|
||||
|
||||
<!-- Vertical slider popover -->
|
||||
<PopoverContent
|
||||
class="w-auto py-4 px-3 h-64 flex items-center justify-center rounded-none border border-black/5 dark:border-white/10 shadow-sm bg-paper dark:bg-dark-card"
|
||||
class="w-auto py-4 px-3 h-64 flex items-center justify-center rounded-none border border-subtle shadow-sm bg-paper dark:bg-dark-card"
|
||||
align="center"
|
||||
side="top"
|
||||
>
|
||||
|
||||
@@ -24,7 +24,7 @@ interface Props {
|
||||
const { label, children, class: className }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class={cn('flex flex-col gap-3 py-6 border-b border-black/5 dark:border-white/10 last:border-0', className)}>
|
||||
<div class={cn('flex flex-col gap-3 py-6 border-b border-subtle last:border-0', className)}>
|
||||
<div class="flex justify-between items-center text-[0.6875rem] font-primary font-bold tracking-tight text-neutral-900 dark:text-neutral-100 uppercase leading-none">
|
||||
{label}
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,10 @@ import type { Snippet } from 'svelte';
|
||||
import { cubicOut } from 'svelte/easing';
|
||||
import type { HTMLInputAttributes } from 'svelte/elements';
|
||||
import { scale } from 'svelte/transition';
|
||||
import {
|
||||
inputSizeConfig,
|
||||
inputVariantConfig,
|
||||
} from './config';
|
||||
import type {
|
||||
InputSize,
|
||||
InputVariant,
|
||||
@@ -80,36 +84,11 @@ let {
|
||||
...rest
|
||||
}: Props = $props();
|
||||
|
||||
const sizeConfig: Record<InputSize, { input: string; text: string; height: string; clearIcon: number }> = {
|
||||
sm: { input: 'px-3 py-1.5', text: 'text-sm', height: 'h-8', clearIcon: 12 },
|
||||
md: { input: 'px-4 py-2', text: 'text-base', height: 'h-10', clearIcon: 14 },
|
||||
lg: { input: 'px-4 py-3', text: 'text-lg md:text-xl', height: 'h-12', clearIcon: 16 },
|
||||
xl: { input: 'px-4 py-3', text: 'text-xl md:text-2xl', height: 'h-14', clearIcon: 18 },
|
||||
};
|
||||
|
||||
const variantConfig: Record<InputVariant, { base: string; focus: string; error: string }> = {
|
||||
default: {
|
||||
base: 'bg-paper dark:bg-paper border border-black/5 dark:border-white/10',
|
||||
focus: 'focus:border-brand focus:ring-1 focus:ring-brand/20',
|
||||
error: 'border-brand ring-1 ring-brand/20',
|
||||
},
|
||||
underline: {
|
||||
base: 'bg-transparent border-0 border-b border-neutral-300 dark:border-neutral-700',
|
||||
focus: 'focus:border-brand',
|
||||
error: 'border-brand',
|
||||
},
|
||||
filled: {
|
||||
base: 'bg-surface dark:bg-paper border border-transparent',
|
||||
focus: 'focus:border-brand focus:ring-1 focus:ring-brand/20',
|
||||
error: 'border-brand ring-1 ring-brand/20',
|
||||
},
|
||||
};
|
||||
|
||||
const hasValue = $derived(value !== undefined && value !== '');
|
||||
const showClear = $derived(showClearButton && hasValue && !!onclear);
|
||||
const hasRightSlot = $derived(!!rightIcon || showClearButton);
|
||||
const cfg = $derived(sizeConfig[size]);
|
||||
const styles = $derived(variantConfig[variant]);
|
||||
const cfg = $derived(inputSizeConfig[size]);
|
||||
const styles = $derived(inputVariantConfig[variant]);
|
||||
|
||||
const inputClasses = $derived(cn(
|
||||
'font-primary rounded-none outline-none transition-all duration-200',
|
||||
@@ -170,7 +149,7 @@ const inputClasses = $derived(cn(
|
||||
<span
|
||||
class={cn(
|
||||
'text-[0.625rem] font-mono tracking-wide px-1',
|
||||
error ? 'text-brand ' : 'text-neutral-500 dark:text-neutral-400',
|
||||
error ? 'text-brand ' : 'text-secondary',
|
||||
)}
|
||||
>
|
||||
{helperText}
|
||||
|
||||
31
src/shared/ui/Input/config.ts
Normal file
31
src/shared/ui/Input/config.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type {
|
||||
InputSize,
|
||||
InputVariant,
|
||||
} from './types';
|
||||
|
||||
/** Size-specific layout classes: padding, text size, height, and clear-icon pixel size. */
|
||||
export const inputSizeConfig: Record<InputSize, { input: string; text: string; height: string; clearIcon: number }> = {
|
||||
sm: { input: 'px-3 py-1.5', text: 'text-sm', height: 'h-8', clearIcon: 12 },
|
||||
md: { input: 'px-4 py-2', text: 'text-base', height: 'h-10', clearIcon: 14 },
|
||||
lg: { input: 'px-4 py-3', text: 'text-lg md:text-xl', height: 'h-12', clearIcon: 16 },
|
||||
xl: { input: 'px-4 py-3', text: 'text-xl md:text-2xl', height: 'h-14', clearIcon: 18 },
|
||||
};
|
||||
|
||||
/** Variant-specific classes: base background/border, focus ring, and error state. */
|
||||
export const inputVariantConfig: Record<InputVariant, { base: string; focus: string; error: string }> = {
|
||||
default: {
|
||||
base: 'bg-paper dark:bg-paper border border-subtle',
|
||||
focus: 'focus:border-brand focus:ring-1 focus:ring-brand/20',
|
||||
error: 'border-brand ring-1 ring-brand/20',
|
||||
},
|
||||
underline: {
|
||||
base: 'bg-transparent border-0 border-b border-neutral-300 dark:border-neutral-700',
|
||||
focus: 'focus:border-brand',
|
||||
error: 'border-brand',
|
||||
},
|
||||
filled: {
|
||||
base: 'bg-surface dark:bg-paper border border-transparent',
|
||||
focus: 'focus:border-brand focus:ring-1 focus:ring-brand/20',
|
||||
error: 'border-brand ring-1 ring-brand/20',
|
||||
},
|
||||
};
|
||||
@@ -84,7 +84,7 @@ function close() {
|
||||
'overflow-hidden',
|
||||
'will-change-[width]',
|
||||
'transition-[width] duration-300 ease-out',
|
||||
'border-r border-black/5 dark:border-white/10',
|
||||
'border-r border-subtle',
|
||||
'bg-surface dark:bg-dark-bg',
|
||||
isOpen ? 'w-80 opacity-100' : 'w-0 opacity-0',
|
||||
'transition-[width,opacity] duration-300 ease-out',
|
||||
|
||||
@@ -70,7 +70,7 @@ let {
|
||||
const isVertical = $derived(orientation === 'vertical');
|
||||
|
||||
const labelClasses = `font-mono text-[0.625rem] tabular-nums shrink-0
|
||||
text-neutral-500 dark:text-neutral-400
|
||||
text-secondary
|
||||
group-hover:text-neutral-700 dark:group-hover:text-neutral-300
|
||||
transition-colors`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user