css refactor

This commit is contained in:
2026-02-19 18:24:36 +03:00
parent d5a5c3b902
commit db47e4ce55
45 changed files with 1798 additions and 1857 deletions

View File

@@ -9,45 +9,51 @@
@INVARIANT: Supports accessible labels and keyboard navigation.
-->
<script lang="ts">
<script>
// [SECTION: IMPORTS]
import { cn } from '$lib/utils.js';
// [/SECTION: IMPORTS]
// [SECTION: PROPS]
/**
* @purpose Define component interface and default values.
* @purpose Define component interface and default values (Svelte 5 Runes).
*/
export let variant: 'primary' | 'secondary' | 'danger' | 'ghost' = 'primary';
export let size: 'sm' | 'md' | 'lg' = 'md';
export let isLoading: boolean = false;
export let disabled: boolean = false;
export let type: 'button' | 'submit' | 'reset' = 'button';
let className: string = "";
export { className as class };
let {
variant = 'primary',
size = 'md',
isLoading = false,
disabled = false,
type = 'button',
class: className = '',
children,
onclick,
...rest
} = $props();
// [/SECTION: PROPS]
const baseStyles = "inline-flex items-center justify-center font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 rounded-md";
const baseStyles = 'inline-flex items-center justify-center font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 rounded-md';
const variants = {
primary: "bg-blue-600 text-white hover:bg-blue-700 focus-visible:ring-blue-500",
secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200 focus-visible:ring-gray-500",
danger: "bg-red-600 text-white hover:bg-red-700 focus-visible:ring-red-500",
ghost: "bg-transparent hover:bg-gray-100 text-gray-700 focus-visible:ring-gray-500"
primary: 'bg-primary text-white hover:bg-primary-hover focus-visible:ring-primary-ring',
secondary: 'bg-secondary text-secondary-text hover:bg-secondary-hover focus-visible:ring-secondary-ring',
danger: 'bg-destructive text-white hover:bg-destructive-hover focus-visible:ring-destructive-ring',
ghost: 'bg-transparent hover:bg-ghost-hover text-ghost-text focus-visible:ring-ghost-ring',
};
const sizes = {
sm: "h-8 px-3 text-xs",
md: "h-10 px-4 py-2 text-sm",
lg: "h-12 px-6 text-base"
sm: 'h-8 px-3 text-xs',
md: 'h-10 px-4 py-2 text-sm',
lg: 'h-12 px-6 text-base',
};
</script>
<!-- [SECTION: TEMPLATE] -->
<button
{type}
class="{baseStyles} {variants[variant]} {sizes[size]} {className}"
class={cn(baseStyles, variants[variant], sizes[size], className)}
disabled={disabled || isLoading}
on:click
{onclick}
{...rest}
>
{#if isLoading}
<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-current" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
@@ -55,7 +61,7 @@
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
{/if}
<slot />
{@render children?.()}
</button>
<!-- [/SECTION: TEMPLATE] -->

View File

@@ -6,31 +6,46 @@
@LAYER: Atom
-->
<script lang="ts">
<script>
// [SECTION: IMPORTS]
import { cn } from "$lib/utils.js";
// [/SECTION: IMPORTS]
// [SECTION: PROPS]
export let title: string = "";
export let padding: 'none' | 'sm' | 'md' | 'lg' = 'md';
let {
title = "",
padding = "md",
class: className = "",
children,
...rest
} = $props();
// [/SECTION: PROPS]
const paddings = {
none: "p-0",
sm: "p-3",
md: "p-6",
lg: "p-8"
lg: "p-8",
};
</script>
<!-- [SECTION: TEMPLATE] -->
<div class="rounded-lg border border-gray-200 bg-white text-gray-950 shadow-sm">
<div
class={cn(
"rounded-lg border border-gray-200 bg-white text-gray-950 shadow-sm",
className,
)}
{...rest}
>
{#if title}
<div class="flex flex-col space-y-1.5 p-6 border-b border-gray-100">
<h3 class="text-lg font-semibold leading-none tracking-tight">{title}</h3>
</div>
{/if}
<div class="{paddings[padding]}">
<slot />
<div class={paddings[padding]}>
{@render children?.()}
</div>
</div>
<!-- [/SECTION: TEMPLATE] -->
<!-- [/DEF:Card:Component] -->
<!-- [/DEF:Card:Component] -->

View File

@@ -8,14 +8,22 @@
@INVARIANT: Consistent spacing and focus states.
-->
<script lang="ts">
<script>
// [SECTION: IMPORTS]
import { cn } from "$lib/utils.js";
// [/SECTION: IMPORTS]
// [SECTION: PROPS]
export let label: string = "";
export let value: string = "";
export let placeholder: string = "";
export let error: string = "";
export let disabled: boolean = false;
export let type: 'text' | 'password' | 'email' | 'number' = 'text';
let {
label = "",
value = $bindable(""),
placeholder = "",
error = "",
disabled = false,
type = "text",
class: className = "",
...rest
} = $props();
// [/SECTION: PROPS]
let id = "input-" + Math.random().toString(36).substr(2, 9);
@@ -28,20 +36,25 @@
{label}
</label>
{/if}
<input
{id}
{type}
{placeholder}
{disabled}
bind:value
class="flex h-10 w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm ring-offset-white file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-gray-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 {error ? 'border-red-500' : ''}"
class={cn(
"flex h-10 w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm ring-offset-white file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-gray-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
error ? "border-destructive" : "",
className,
)}
{...rest}
/>
{#if error}
<span class="text-xs text-red-500">{error}</span>
<span class="text-xs text-destructive">{error}</span>
{/if}
</div>
<!-- [/SECTION: TEMPLATE] -->
<!-- [/DEF:Input:Component] -->
<!-- [/DEF:Input:Component] -->

View File

@@ -7,25 +7,22 @@
@RELATION: BINDS_TO -> i18n.locale
-->
<script lang="ts">
<script>
// [SECTION: IMPORTS]
import { locale } from '$lib/i18n';
import Select from './Select.svelte';
import { locale } from "$lib/i18n";
import Select from "./Select.svelte";
// [/SECTION: IMPORTS]
const options = [
{ value: 'ru', label: 'Русский' },
{ value: 'en', label: 'English' }
{ value: "ru", label: "Русский" },
{ value: "en", label: "English" },
];
</script>
<!-- [SECTION: TEMPLATE] -->
<div class="w-32">
<Select
bind:value={$locale}
{options}
/>
<Select bind:value={$locale} {options} />
</div>
<!-- [/SECTION: TEMPLATE] -->
<!-- [/DEF:LanguageSwitcher:Component] -->
<!-- [/DEF:LanguageSwitcher:Component] -->

View File

@@ -6,22 +6,35 @@
@LAYER: Atom
-->
<script lang="ts">
<script>
// [SECTION: IMPORTS]
import { cn } from "$lib/utils.js";
// [/SECTION: IMPORTS]
// [SECTION: PROPS]
export let title: string = "";
let {
title = "",
class: className = "",
subtitle,
actions,
...rest
} = $props();
// [/SECTION: PROPS]
</script>
<!-- [SECTION: TEMPLATE] -->
<header class="flex items-center justify-between mb-8">
<header
class={cn("flex items-center justify-between mb-8", className)}
{...rest}
>
<div class="space-y-1">
<h1 class="text-3xl font-bold tracking-tight text-gray-900">{title}</h1>
<slot name="subtitle" />
{@render subtitle?.()}
</div>
<div class="flex items-center gap-4">
<slot name="actions" />
{@render actions?.()}
</div>
</header>
<!-- [/SECTION: TEMPLATE] -->
<!-- [/DEF:PageHeader:Component] -->
<!-- [/DEF:PageHeader:Component] -->

View File

@@ -6,12 +6,20 @@
@LAYER: Atom
-->
<script lang="ts">
<script>
// [SECTION: IMPORTS]
import { cn } from "$lib/utils.js";
// [/SECTION: IMPORTS]
// [SECTION: PROPS]
export let label: string = "";
export let value: string | number = "";
export let options: Array<{ value: string | number, label: string }> = [];
export let disabled: boolean = false;
let {
label = "",
value = $bindable(""),
options = [],
disabled = false,
class: className = "",
...rest
} = $props();
// [/SECTION: PROPS]
let id = "select-" + Math.random().toString(36).substr(2, 9);
@@ -29,7 +37,11 @@
{id}
{disabled}
bind:value
class="flex h-10 w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm ring-offset-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
class={cn(
"flex h-10 w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm ring-offset-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
{...rest}
>
{#each options as option}
<option value={option.value}>{option.label}</option>
@@ -38,4 +50,4 @@
</div>
<!-- [/SECTION: TEMPLATE] -->
<!-- [/DEF:Select:Component] -->
<!-- [/DEF:Select:Component] -->