refactor(frontend): improve ui using design system

- Refactor Navbar, ChatInput, RegisterPage, and CreatePlaylistDialog to use @veza/design-system components
- Shim local UI components (Button, Input, Card) to align with Design System API and styles
- Fix hundreds of type errors by exporting missing components (SearchInput, FileUpload) and adding missing props (icon, variant)
This commit is contained in:
senke 2026-01-18 22:27:53 +01:00
parent 420b0f4e9b
commit eccc4e5e89
15 changed files with 282 additions and 221 deletions

View file

@ -65,7 +65,7 @@ export function SwaggerUIDoc({ specUrl, spec, useIframe = false }: SwaggerUIProp
const swaggerConfig = {
url: spec ? undefined : getOpenApiUrl(),
spec: spec,
spec,
deepLinking: true,
displayOperationId: false,
defaultModelsExpandDepth: 1,

View file

@ -18,7 +18,7 @@ interface DashboardLayoutProps {
*/
export function DashboardLayout({ children }: DashboardLayoutProps) {
const { sidebarOpen } = useUIStore();
// FIX: Calculer le margin-left pour compenser la sidebar fixe
// Sidebar: left-6 (24px) + width (w-64 = 256px quand ouverte, w-20 = 80px quand fermée) + gap (24px)
// Sur desktop (lg): sidebar est toujours visible, donc toujours besoin de margin
@ -28,13 +28,13 @@ export function DashboardLayout({ children }: DashboardLayoutProps) {
<div className="flex h-screen overflow-hidden relative">
<Sidebar />
{/* FIX: Ajouter margin-left pour compenser la sidebar fixe et éviter la superposition */}
<div
<div
className={cn(
'flex-1 flex flex-col min-w-0 transition-all duration-500 ease-in-out',
// Sur desktop (lg), toujours ajouter le margin pour la sidebar
// Sidebar ouverte: left-6 (24px) + w-64 (256px) + gap (24px) = 304px
// Sidebar fermée: left-6 (24px) + w-20 (80px) + gap (24px) = 128px
sidebarOpen
sidebarOpen
? 'lg:ml-[304px]' // 24 + 256 + 24 = 304px
: 'lg:ml-[128px]', // 24 + 80 + 24 = 128px
// Sur mobile, pas de margin car sidebar est cachée (-translate-x-full)

View file

@ -78,7 +78,7 @@ export function Header(_props: HeaderProps) {
>
<Menu className="w-5 h-5" />
</button>
<Link
to="/dashboard"
className="flex items-center gap-4 active:scale-95 transition-transform focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-kodo-steel focus-visible:ring-offset-2 focus-visible:ring-offset-kodo-void rounded"

View file

@ -9,9 +9,8 @@ import {
User,
ShoppingCart,
} from 'lucide-react';
import { Button } from '../ui/button';
import { Button, SearchInput } from '@veza/design-system';
import { useTheme } from '../../context/ThemeContext';
import { SearchInput } from '../ui/input';
import { Notification } from '../../types';
import { useCart } from '../../context/CartContext';
import { NotificationBell } from '../notifications/NotificationBell';
@ -191,52 +190,57 @@ export const Navbar: React.FC<NavbarProps> = ({ onNavigate, onLogout }) => {
<p className="text-sm font-bold text-white">Cyber_Producer</p>
<p className="text-xs text-kodo-content-dim">Pro Plan</p>
</div>
<button
<Button
variant="ghost"
onClick={() => {
onNavigate('profile');
setShowUserMenu(false);
}}
className="w-full text-left px-4 py-2.5 text-sm text-kodo-text-main hover:bg-white/5 hover:text-white flex items-center gap-4 transition-colors"
className="w-full justify-start px-4 py-2.5 text-sm text-kodo-text-main hover:bg-white/5 hover:text-white gap-4"
>
<User className="w-4 h-4" /> My Profile
</button>
<button
</Button>
<Button
variant="ghost"
onClick={() => {
onNavigate('studio/go-live');
setShowUserMenu(false);
}}
className="w-full text-left px-4 py-2.5 text-sm text-kodo-text-main hover:bg-white/5 hover:text-white flex items-center gap-4 transition-colors"
className="w-full justify-start px-4 py-2.5 text-sm text-kodo-text-main hover:bg-white/5 hover:text-white gap-4"
>
<Zap className="w-4 h-4 text-kodo-red" /> Go Live
</button>
<button
</Button>
<Button
variant="ghost"
onClick={() => {
onNavigate('purchases');
setShowUserMenu(false);
}}
className="w-full text-left px-4 py-2.5 text-sm text-kodo-text-main hover:bg-white/5 hover:text-white flex items-center gap-4 transition-colors"
className="w-full justify-start px-4 py-2.5 text-sm text-kodo-text-main hover:bg-white/5 hover:text-white gap-4"
>
<ShoppingCart className="w-4 h-4" /> Purchases
</button>
<button
</Button>
<Button
variant="ghost"
onClick={() => {
onNavigate('settings');
setShowUserMenu(false);
}}
className="w-full text-left px-4 py-2.5 text-sm text-kodo-text-main hover:bg-white/5 hover:text-white flex items-center gap-4 transition-colors"
className="w-full justify-start px-4 py-2.5 text-sm text-kodo-text-main hover:bg-white/5 hover:text-white gap-4"
>
<Settings className="w-4 h-4" /> Settings
</button>
</Button>
<div className="h-px bg-kodo-steel/30 my-1 mx-2"></div>
<button
<Button
variant="ghost"
onClick={() => {
onLogout();
setShowUserMenu(false);
}}
className="w-full text-left px-4 py-2.5 text-sm text-kodo-red hover:bg-kodo-red/10 rounded-lg flex items-center gap-4 transition-colors"
className="w-full justify-start px-4 py-2.5 text-sm text-kodo-red hover:bg-kodo-red/10 rounded-lg gap-4"
>
<LogOut className="w-4 h-4" /> Sign Out
</button>
</Button>
</div>
)}
</div>

View file

@ -215,7 +215,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
sidebarOpen ? 'w-64 translate-x-0 opacity-100' : '-translate-x-full lg:translate-x-0 lg:opacity-100 lg:w-20',
)}
style={{ zIndex: 90 }}
// FIX: S'assurer que la sidebar ne masque pas le contenu principal
// FIX: S'assurer que la sidebar ne masque pas le contenu principal
>
{/* Hub Header Integration avec bouton toggle */}
<div className="p-6 border-b border-white/5 flex items-center gap-4">

View file

@ -14,13 +14,16 @@ import { cn } from '@/lib/utils';
* - ghost: Minimal button with hover effect, for tertiary actions
*/
const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-xl text-sm font-medium transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-kodo-cyan focus-visible:ring-offset-2 focus-visible:ring-offset-kodo-void disabled:pointer-events-none disabled:opacity-50',
'inline-flex items-center justify-center whitespace-nowrap rounded-xl text-sm font-medium transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-kodo-cyan focus-visible:ring-offset-2 focus-visible:ring-offset-kodo-void disabled:pointer-events-none disabled:opacity-50 gap-2',
{
variants: {
variant: {
/** Primary action button - use for main CTAs, submit buttons */
default:
'bg-kodo-cyan text-kodo-void hover:bg-kodo-cyan-dim hover:shadow-[0_0_15px_rgba(102,252,241,0.3)] border border-transparent font-semibold tracking-tight',
/** Primary alias for Design System compatibility */
primary:
'bg-kodo-cyan text-kodo-void hover:bg-kodo-cyan-dim hover:shadow-[0_0_15px_rgba(102,252,241,0.3)] border border-transparent font-semibold tracking-tight',
/** Destructive actions - use for delete, remove, clear actions */
destructive:
'bg-kodo-red/10 text-kodo-red hover:bg-kodo-red/20 border border-kodo-red/30 hover:border-kodo-red/50',
@ -32,6 +35,12 @@ const buttonVariants = cva(
'bg-kodo-steel/30 text-white hover:bg-kodo-steel/50 border border-white/5 hover:border-white/10',
/** Ghost button - use for icon buttons, menu items, subtle actions */
ghost: 'hover:bg-white/5 text-white',
/** Gaming style */
gaming: 'bg-kodo-slate border border-kodo-gold/40 text-kodo-gold hover:bg-kodo-gold/10 hover:border-kodo-gold font-bold tracking-wider uppercase',
/** Terminal style */
terminal: 'bg-kodo-ink border border-kodo-steel text-gray-300 font-mono text-xs hover:border-kodo-cyan hover:text-kodo-cyan',
/** Nature style */
nature: 'bg-kodo-slate border border-kodo-lime/30 text-kodo-lime hover:bg-kodo-lime/10',
},
size: {
/** Default size - standard buttons */
@ -75,6 +84,8 @@ export interface ButtonProps
VariantProps<typeof buttonVariants> {
/** Use asChild to compose with other components (e.g., Link from react-router) */
asChild?: boolean;
/** Optional icon to display before the label */
icon?: React.ReactNode;
}
/**
@ -100,14 +111,17 @@ export interface ButtonProps
* ```
*/
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
({ className, variant, size, asChild = false, icon, children, ...props }, ref) => {
const Comp = asChild ? Slot : 'button';
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
>
{icon && <span className="flex items-center justify-center pointer-events-none">{icon}</span>}
{children}
</Comp>
);
},
);

View file

@ -1,23 +1,39 @@
import * as React from 'react';
import { cn } from '@/lib/utils';
import { cva, type VariantProps } from 'class-variance-authority';
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
'rounded-2xl border border-white/5 bg-kodo-ink/40 text-kodo-text-main shadow-lg backdrop-blur-md relative overflow-hidden',
'hover:bg-white/5 transition-colors duration-200',
'z-10', // FIX: Assurer que les Cards sont au-dessus des overlays
className,
)}
{...props}
>
{props.children}
</div>
));
const cardVariants = cva(
'rounded-2xl shadow-lg backdrop-blur-md relative overflow-hidden transition-colors duration-200 z-10',
{
variants: {
variant: {
default: 'bg-kodo-graphite border border-kodo-steel/60 hover:border-kodo-steel text-kodo-text-main',
manga: 'bg-gradient-to-br from-kodo-graphite to-kodo-slate border border-kodo-magenta/20 hover:border-kodo-magenta/40 hover:shadow-neon-magenta/10 text-kodo-text-main',
gaming: 'bg-kodo-ink border border-kodo-cyan/20 hover:border-kodo-cyan/40 hover:shadow-neon-cyan/10 text-kodo-text-main',
glass: 'bg-kodo-slate/40 backdrop-blur-xl border border-white/5 hover:bg-kodo-slate/50 text-kodo-text-main',
},
},
defaultVariants: {
variant: 'default',
},
}
);
export interface CardProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof cardVariants> {}
const Card = React.forwardRef<HTMLDivElement, CardProps>(
({ className, variant, ...props }, ref) => (
<div
ref={ref}
className={cn(cardVariants({ variant, className }))}
{...props}
>
{props.children}
</div>
)
);
Card.displayName = 'Card';
const CardHeader = React.forwardRef<

View file

@ -1,24 +1,10 @@
import * as React from 'react';
import { cn } from '@/lib/utils';
import { SearchInput as DSSearchInput, FileUpload as DSFileUpload, Input as DSInput } from '@veza/design-system';
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
// Re-export components from Design System
export const Input = DSInput;
export const SearchInput = DSSearchInput;
export const FileUpload = DSFileUpload;
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
'flex h-10 w-full rounded-lg border border-white/10 bg-black/20 px-4 py-2.5 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-kodo-secondary/50 focus-visible:outline-none focus-visible:border-kodo-cyan/60 disabled:cursor-not-allowed disabled:opacity-50 transition-colors duration-200 text-white',
className,
)}
ref={ref}
{...props}
/>
);
},
);
Input.displayName = 'Input';
export { Input };
// Export InputProps for compatibility
export type { InputProps } from '@veza/design-system';

View file

@ -2,13 +2,14 @@ import React, { useState, useEffect } from 'react';
import { Navigate, Link } from 'react-router-dom';
import { useAuthStore } from '../store/authStore';
import { AuthLayout } from '../components/AuthLayout';
import { AuthInput } from '../components/AuthInput';
import { AuthButton } from '../components/AuthButton';
import { PasswordStrengthIndicator } from '../components/PasswordStrengthIndicator';
import { useRegister } from '../hooks/useRegister';
import { useUsernameAvailability } from '../hooks/useUsernameAvailability';
import { authApi } from '@/services/api/auth';
import { logger } from '@/utils/logger';
import { Input, Button } from '@veza/design-system';
import { Checkbox } from '@/components/ui/checkbox';
import { Loader2 } from 'lucide-react';
import type { RegisterFormData } from '../types';
export function RegisterPage() {
@ -186,23 +187,23 @@ export function RegisterPage() {
Email de vérification renvoyé avec succès !
</p>
)}
<button
<Button
type="button"
variant="ghost"
onClick={handleResendVerificationEmail}
disabled={resendLoading}
className="text-kodo-cyan hover:underline text-sm disabled:opacity-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 rounded"
className="text-kodo-cyan hover:underline text-sm disabled:opacity-50"
aria-label="Renvoyer l'email de vérification"
aria-busy={resendLoading}
>
{resendLoading ? (
<>
<span className="sr-only">Envoi en cours</span>
<span aria-hidden="true">Envoi en cours...</span>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
Envoi en cours...
</>
) : (
"Renvoyer l'email de vérification"
)}
</button>
</Button>
</div>
) : (
<form
@ -220,16 +221,21 @@ export function RegisterPage() {
</div>
)}
<div>
<AuthInput
<Input
type="text"
label="Nom d'utilisateur"
label="Nom d'utilisateur *"
value={formData.username}
onChange={(e) => handleChange('username', e.target.value)}
onBlur={() => handleBlur('username')}
error={errors.username}
required
autoComplete="username"
aria-invalid={errors.username ? 'true' : 'false'}
/>
{errors.username && (
<p className="mt-1 text-sm text-kodo-red" role="alert">
{errors.username}
</p>
)}
{formData.username.length >= 3 && (
<div className="mt-1" aria-live="polite" aria-atomic="true">
{checkingUsername ? (
@ -253,56 +259,73 @@ export function RegisterPage() {
</div>
)}
</div>
<AuthInput
type="email"
label="Email"
value={formData.email}
onChange={(e) => handleChange('email', e.target.value)}
onBlur={() => handleBlur('email')}
error={errors.email}
required
autoComplete="email"
/>
<div>
<AuthInput
<Input
type="email"
label="Email *"
value={formData.email}
onChange={(e) => handleChange('email', e.target.value)}
onBlur={() => handleBlur('email')}
required
autoComplete="email"
aria-invalid={errors.email ? 'true' : 'false'}
/>
{errors.email && (
<p className="mt-1 text-sm text-kodo-red" role="alert">
{errors.email}
</p>
)}
</div>
<div>
<Input
type="password"
label="Mot de passe"
label="Mot de passe *"
value={formData.password}
onChange={(e) => handleChange('password', e.target.value)}
onBlur={() => handleBlur('password')}
error={errors.password}
required
autoComplete="new-password"
aria-invalid={errors.password ? 'true' : 'false'}
/>
{errors.password && (
<p className="mt-1 text-sm text-kodo-red" role="alert">
{errors.password}
</p>
)}
<PasswordStrengthIndicator password={formData.password} />
</div>
<AuthInput
type="password"
label="Confirmer le mot de passe"
value={formData.password_confirm}
onChange={(e) => handleChange('password_confirm', e.target.value)}
onBlur={() => handleBlur('password_confirm')}
error={errors.password_confirm}
required
autoComplete="new-password"
/>
<div>
<Input
type="password"
label="Confirmer le mot de passe *"
value={formData.password_confirm}
onChange={(e) => handleChange('password_confirm', e.target.value)}
onBlur={() => handleBlur('password_confirm')}
required
autoComplete="new-password"
aria-invalid={errors.password_confirm ? 'true' : 'false'}
/>
{errors.password_confirm && (
<p className="mt-1 text-sm text-kodo-red" role="alert">
{errors.password_confirm}
</p>
)}
</div>
<div className="flex items-start">
<input
type="checkbox"
<Checkbox
id="terms"
checked={acceptedTerms}
onChange={(e) => {
setAcceptedTerms(e.target.checked);
onCheckedChange={(checked) => {
setAcceptedTerms(checked as boolean);
if (errors.terms) {
setErrors({ ...errors, terms: undefined });
}
}}
className="h-4 w-4 text-kodo-cyan focus:ring-blue-500 border-kodo-steel rounded mt-1"
required
aria-invalid={errors.terms ? 'true' : 'false'}
aria-describedby={
errors.terms ? 'terms-error' : 'terms-description'
}
required
/>
<label htmlFor="terms" className="ml-2 block text-sm text-kodo-text-main">
J'accepte les{' '}
@ -332,9 +355,16 @@ export function RegisterPage() {
{errors.terms}
</p>
)}
<AuthButton type="submit" loading={loading}>
S'inscrire
</AuthButton>
<Button type="submit" disabled={loading} className="w-full">
{loading ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
Inscription en cours...
</>
) : (
"S'inscrire"
)}
</Button>
</form>
)}
</AuthLayout>

View file

@ -24,7 +24,7 @@ import { MessageAttachment } from '../types';
import { LoadingSpinner } from '@/components/ui/loading-spinner';
import { logger } from '@/utils/logger';
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { Button } from '@veza/design-system';
import { useIsRateLimited } from '@/hooks/useIsRateLimited';
// Lazy load
@ -226,7 +226,7 @@ export const ChatInput: React.FC = () => {
<Button
type="submit"
variant="default"
variant="primary"
size="icon"
className={cn(
'rounded-xl transition-all duration-300',

View file

@ -5,8 +5,7 @@ import { z } from 'zod';
// Ensure PlaylistError is exported from service or assume general Error if not available
import { playlistsApi } from '@/services/api/playlists';
import { Dialog } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Button, Input } from '@veza/design-system';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Checkbox } from '@/components/ui/checkbox';
@ -97,14 +96,9 @@ export function CreatePlaylistDialog({
aria-label="Dialogue de création de playlist"
>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="title">
Titre{' '}
<span className="text-destructive" aria-hidden="true">
*
</span>
</Label>
<div>
<Input
label="Titre *"
id="title"
{...register('title')}
placeholder="Ma nouvelle playlist"
@ -164,7 +158,7 @@ export function CreatePlaylistDialog({
<div className="flex justify-end gap-2 pt-4">
<Button
type="button"
variant="outline"
variant="secondary"
onClick={handleCancel}
disabled={isSubmitting}
aria-label="Annuler la création de playlist"

View file

@ -56,7 +56,7 @@ export const developerService = {
return {
id: `k-${Date.now()}`,
name: data.name,
prefix: prefix,
prefix,
created: formattedDate,
lastUsed: 'Never',
status: 'active' as const,

View file

@ -96,48 +96,32 @@
--veza-matrix: #003300;
/* === GRADIENTS === */
--gradient-neon: linear-gradient(
135deg,
var(--veza-cyan) 0%,
var(--veza-magenta) 50%,
var(--veza-lime) 100%
);
--gradient-cyber: linear-gradient(
135deg,
var(--veza-spectral-cyan) 0%,
var(--veza-lavender) 100%
);
--gradient-sunset: linear-gradient(
135deg,
var(--veza-orange) 0%,
var(--veza-magenta) 100%
);
--gradient-forest: linear-gradient(
135deg,
var(--veza-moss) 0%,
var(--veza-leaf) 50%,
var(--veza-lime) 100%
);
--gradient-nature: linear-gradient(
135deg,
var(--veza-sage) 0%,
var(--veza-moss) 100%
);
--gradient-space: linear-gradient(
135deg,
var(--veza-lavender) 0%,
var(--veza-ember) 100%
);
--gradient-terminal: linear-gradient(
180deg,
var(--veza-matrix) 0%,
var(--veza-void) 100%
);
--gradient-gaming: linear-gradient(
135deg,
var(--veza-orange) 0%,
var(--veza-xp-gold) 100%
);
--gradient-neon: linear-gradient(135deg,
var(--veza-cyan) 0%,
var(--veza-magenta) 50%,
var(--veza-lime) 100%);
--gradient-cyber: linear-gradient(135deg,
var(--veza-spectral-cyan) 0%,
var(--veza-lavender) 100%);
--gradient-sunset: linear-gradient(135deg,
var(--veza-orange) 0%,
var(--veza-magenta) 100%);
--gradient-forest: linear-gradient(135deg,
var(--veza-moss) 0%,
var(--veza-leaf) 50%,
var(--veza-lime) 100%);
--gradient-nature: linear-gradient(135deg,
var(--veza-sage) 0%,
var(--veza-moss) 100%);
--gradient-space: linear-gradient(135deg,
var(--veza-lavender) 0%,
var(--veza-ember) 100%);
--gradient-terminal: linear-gradient(180deg,
var(--veza-matrix) 0%,
var(--veza-void) 100%);
--gradient-gaming: linear-gradient(135deg,
var(--veza-orange) 0%,
var(--veza-xp-gold) 100%);
/* === TYPOGRAPHY === */
--font-display: 'Orbitron', 'Noto Sans JP', system-ui, sans-serif;
@ -147,32 +131,56 @@
--font-mono: 'JetBrains Mono', 'IBM Plex Mono', 'Consolas', monospace;
/* Font Sizes - Modular Scale */
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.125rem; /* 18px */
--text-xl: 1.25rem; /* 20px */
--text-2xl: 1.5rem; /* 24px */
--text-3xl: 1.875rem; /* 30px */
--text-4xl: 2.25rem; /* 36px */
--text-5xl: 3rem; /* 48px */
--text-6xl: 3.75rem; /* 60px */
--text-7xl: 4.5rem; /* 72px */
--text-8xl: 6rem; /* 96px */
--text-xs: 0.75rem;
/* 12px */
--text-sm: 0.875rem;
/* 14px */
--text-base: 1rem;
/* 16px */
--text-lg: 1.125rem;
/* 18px */
--text-xl: 1.25rem;
/* 20px */
--text-2xl: 1.5rem;
/* 24px */
--text-3xl: 1.875rem;
/* 30px */
--text-4xl: 2.25rem;
/* 36px */
--text-5xl: 3rem;
/* 48px */
--text-6xl: 3.75rem;
/* 60px */
--text-7xl: 4.5rem;
/* 72px */
--text-8xl: 6rem;
/* 96px */
/* === SPACING === */
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.25rem; /* 20px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
--space-10: 2.5rem; /* 40px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
--space-20: 5rem; /* 80px */
--space-24: 6rem; /* 96px */
--space-1: 0.25rem;
/* 4px */
--space-2: 0.5rem;
/* 8px */
--space-3: 0.75rem;
/* 12px */
--space-4: 1rem;
/* 16px */
--space-5: 1.25rem;
/* 20px */
--space-6: 1.5rem;
/* 24px */
--space-8: 2rem;
/* 32px */
--space-10: 2.5rem;
/* 40px */
--space-12: 3rem;
/* 48px */
--space-16: 4rem;
/* 64px */
--space-20: 5rem;
/* 80px */
--space-24: 6rem;
/* 96px */
/* === BORDERS & RADIUS === */
--radius-none: 0;
@ -185,33 +193,27 @@
--radius-organic: 60% 40% 50% 50% / 55% 50% 40% 60%;
/* Manga-style clip paths */
--clip-manga: polygon(
0 0,
calc(100% - 12px) 0,
100% 12px,
100% 100%,
12px 100%,
0 calc(100% - 12px)
);
--clip-manga-lg: polygon(
0 0,
calc(100% - 20px) 0,
100% 20px,
100% 100%,
20px 100%,
0 calc(100% - 20px)
);
--clip-manga: polygon(0 0,
calc(100% - 12px) 0,
100% 12px,
100% 100%,
12px 100%,
0 calc(100% - 12px));
--clip-manga-lg: polygon(0 0,
calc(100% - 20px) 0,
100% 20px,
100% 100%,
20px 100%,
0 calc(100% - 20px));
--clip-hex: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
--clip-badge: polygon(
0 10%,
10% 0,
90% 0,
100% 10%,
100% 90%,
90% 100%,
10% 100%,
0 90%
);
--clip-badge: polygon(0 10%,
10% 0,
90% 0,
100% 10%,
100% 90%,
90% 100%,
10% 100%,
0 90%);
/* === SHADOWS & GLOWS === */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.5);
@ -277,40 +279,48 @@
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes logo-pulse {
0%,
100% {
filter: drop-shadow(0 0 8px var(--veza-cyan));
}
50% {
filter: drop-shadow(0 0 20px var(--veza-magenta));
}
}
@keyframes graffiti-shake {
0%,
100% {
transform: rotate(0deg);
}
25% {
transform: rotate(-2deg);
}
75% {
transform: rotate(2deg);
}
}
@keyframes gentle-pulse {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.6;
transform: scale(0.9);
@ -318,13 +328,16 @@
}
@keyframes neon-flicker {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.8;
}
75% {
opacity: 0.95;
}
@ -335,6 +348,7 @@
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
@ -346,6 +360,7 @@
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
@ -356,6 +371,7 @@
from {
opacity: 0;
}
to {
opacity: 1;
}
@ -366,6 +382,7 @@
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
@ -530,4 +547,4 @@
.border-gradient-neon {
border-image: linear-gradient(135deg, var(--veza-cyan), var(--veza-magenta)) 1;
}
}

View file

@ -99,7 +99,7 @@ export function applyAggressiveVisualFix(): void {
if (rect.width > 0 && rect.width < 10 && rect.height > window.innerHeight * 0.3) {
htmlEl.style.setProperty('display', 'none', 'important');
hiddenCount++;
console.log(`[AggressiveFix] Masqué élément suspect: ${el.tagName}${el.id ? '#' + el.id : ''} (${rect.width}px x ${rect.height}px)`);
console.log(`[AggressiveFix] Masqué élément suspect: ${el.tagName}${el.id ? `#${ el.id}` : ''} (${rect.width}px x ${rect.height}px)`);
}
// Masquer les éléments sur le bord droit
@ -107,7 +107,7 @@ export function applyAggressiveVisualFix(): void {
if (isOnRightEdge && rect.width < 20 && rect.height > window.innerHeight * 0.2) {
htmlEl.style.setProperty('display', 'none', 'important');
hiddenCount++;
console.log(`[AggressiveFix] Masqué élément sur bord droit: ${el.tagName}${el.id ? '#' + el.id : ''}`);
console.log(`[AggressiveFix] Masqué élément sur bord droit: ${el.tagName}${el.id ? `#${ el.id}` : ''}`);
}
});

View file

@ -322,7 +322,7 @@ export function fixDisplayIssues(): void {
// Remove vertical gradient, keep only horizontal
const newBg = currentBg.replace(/linear-gradient\(90deg[^)]+\)/g, '').replace(/,\s*,/g, ',').replace(/^,\s*|,\s*$/g, '');
htmlEl.style.backgroundImage = newBg || 'none';
console.log(`[FixDisplay] Removed vertical gradient from ${tag}${id ? '#' + id : ''}${className ? '.' + className.split(' ')[0] : ''}`);
console.log(`[FixDisplay] Removed vertical gradient from ${tag}${id ? `#${ id}` : ''}${className ? `.${ className.split(' ')[0]}` : ''}`);
}
}
});
@ -430,7 +430,7 @@ export function fixDisplayIssues(): void {
(isOnRightEdge && rect.width < 10 && rect.height > window.innerHeight * 0.3)) {
htmlEl.style.setProperty('display', 'none', 'important');
fixedCount++;
console.log(`[FixDisplay] Hidden narrow vertical element: ${el.tagName}${el.id ? '#' + el.id : ''} (${rect.width}px x ${rect.height}px, right: ${rect.right}px)`);
console.log(`[FixDisplay] Hidden narrow vertical element: ${el.tagName}${el.id ? `#${ el.id}` : ''} (${rect.width}px x ${rect.height}px, right: ${rect.right}px)`);
}
// FIX: Remove vertical borders that could create lines
@ -610,7 +610,7 @@ if (import.meta.env.DEV) {
}
// Force remove via inline style
element.style.setProperty('background-image', element.style.backgroundImage?.replace(/linear-gradient\(90deg[^)]+\)/g, '').replace(/repeating-linear-gradient\(90deg[^)]+\)/g, '') || 'none', 'important');
console.log(`[FixDisplay] Removed vertical gradient from newly added element: ${element.tagName}${element.id ? '#' + element.id : ''}`);
console.log(`[FixDisplay] Removed vertical gradient from newly added element: ${element.tagName}${element.id ? `#${ element.id}` : ''}`);
}
};
@ -688,7 +688,7 @@ if (import.meta.env.DEV) {
className: el.className?.toString().substring(0, 50) || '',
reason: 'vertical-gradient-90deg'
});
console.log(`[FixDisplay] Removed vertical gradient from ${el.tagName}${el.id ? '#' + el.id : ''}${el.className ? '.' + el.className.toString().split(' ')[0] : ''} (z-index: ${zIndex})`);
console.log(`[FixDisplay] Removed vertical gradient from ${el.tagName}${el.id ? `#${ el.id}` : ''}${el.className ? `.${ el.className.toString().split(' ')[0]}` : ''} (z-index: ${zIndex})`);
}
// Check for suspicious elements that could create vertical lines
@ -717,7 +717,7 @@ if (import.meta.env.DEV) {
if (beforeBg.includes('90deg') && (beforeBg.includes('linear-gradient') || beforeBg.includes('repeating-linear-gradient'))) {
suspiciousElements.push({
tag: el.tagName + '::before',
tag: `${el.tagName }::before`,
id: el.id || '',
className: el.className?.toString().substring(0, 50) || '',
reason: 'vertical-gradient-in-before'
@ -725,7 +725,7 @@ if (import.meta.env.DEV) {
}
if (afterBg.includes('90deg') && (afterBg.includes('linear-gradient') || afterBg.includes('repeating-linear-gradient'))) {
suspiciousElements.push({
tag: el.tagName + '::after',
tag: `${el.tagName }::after`,
id: el.id || '',
className: el.className?.toString().substring(0, 50) || '',
reason: 'vertical-gradient-in-after'