feat(design): neon polish — rating bars, destructive colors, active nav glow
This commit is contained in:
@@ -38,9 +38,9 @@ export function MiniBars({
|
||||
<span className="text-[10px] uppercase tracking-wide text-muted-foreground w-[3.5rem] shrink-0">
|
||||
{label}
|
||||
</span>
|
||||
<div className="flex-1 h-1.5 rounded-full bg-muted overflow-hidden">
|
||||
<div className="flex-1 h-1.5 rounded-full bg-foreground/5 overflow-hidden">
|
||||
<div
|
||||
className="h-full rounded-full bg-primary/70"
|
||||
className="neon-bar h-full rounded-full"
|
||||
style={{ width: `${pct(value)}%` }}
|
||||
/>
|
||||
</div>
|
||||
@@ -67,17 +67,17 @@ export function MiniBarStack({
|
||||
const a = num(usability);
|
||||
return (
|
||||
<div
|
||||
className={cn("h-1.5 w-16 rounded-full bg-muted overflow-hidden flex", className)}
|
||||
className={cn("h-1.5 w-16 rounded-full bg-foreground/5 overflow-hidden flex", className)}
|
||||
title={`Usefulness ${u != null ? u.toFixed(1) : "N/A"} / Usability ${
|
||||
a != null ? a.toFixed(1) : "N/A"
|
||||
} (of 5)`}
|
||||
>
|
||||
<div
|
||||
className="h-full bg-primary/80"
|
||||
className="neon-bar h-full"
|
||||
style={{ width: `${pct(usefulness)}%` }}
|
||||
/>
|
||||
<div
|
||||
className="h-full bg-primary/40"
|
||||
className="neon-bar h-full opacity-60"
|
||||
style={{ width: `${pct(usability)}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -12,9 +12,9 @@ const buttonVariants = cva(
|
||||
variant: {
|
||||
default:
|
||||
// @replit: no hover, and add primary border
|
||||
"bg-primary text-primary-foreground border border-primary-border hover:neon-glow-primary focus-visible:neon-glow-primary transition-shadow",
|
||||
"bg-primary text-primary-foreground border border-primary-border neon-glow-primary hover:brightness-105 focus-visible:brightness-105 transition-[box-shadow,filter]",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground shadow-sm border-destructive-border",
|
||||
"bg-destructive text-destructive-foreground shadow-sm border-destructive-border shadow-[0_0_12px_-2px_hsl(var(--destructive)/0.5)] hover:brightness-105 transition-[box-shadow,filter]",
|
||||
outline:
|
||||
// @replit Shows the background color of whatever card / sidebar / accent background it is inside of.
|
||||
// Inherits the current text color. Uses shadow-xs. no shadow on active
|
||||
|
||||
@@ -8,7 +8,7 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:neon-glow disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary focus-visible:neon-glow disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
|
||||
@@ -12,13 +12,13 @@ const Progress = React.forwardRef<
|
||||
<ProgressPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative h-2 w-full overflow-hidden rounded-full border border-border/60 bg-primary/10",
|
||||
"relative h-2 w-full overflow-hidden rounded-full border border-border/60 bg-foreground/5",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ProgressPrimitive.Indicator
|
||||
className="loading-gradient h-full w-full flex-1 transition-all"
|
||||
className="neon-bar h-full w-full flex-1 transition-all"
|
||||
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
||||
/>
|
||||
</ProgressPrimitive.Root>
|
||||
|
||||
@@ -487,7 +487,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
|
||||
}
|
||||
|
||||
const sidebarMenuButtonVariants = cva(
|
||||
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:w-8! group-data-[collapsible=icon]:h-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[active=true]:neon-glow-primary data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:w-8! group-data-[collapsible=icon]:h-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
@@ -530,7 +530,7 @@ function SidebarMenuButton({
|
||||
data-sidebar="menu-button"
|
||||
data-size={size}
|
||||
data-active={isActive}
|
||||
className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
|
||||
className={cn("neon-sidebar-active", sidebarMenuButtonVariants({ variant, size }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ const Textarea = React.forwardRef<
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:neon-glow disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary focus-visible:neon-glow disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
|
||||
@@ -216,7 +216,7 @@
|
||||
--accent: 217 33% 17%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive: 0 72% 48%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--input: 217 33% 17%;
|
||||
@@ -397,6 +397,19 @@
|
||||
0 0 34px -8px hsl(var(--glow-3) / 0.3);
|
||||
}
|
||||
|
||||
/* Solid neon fill for progress/rating bars — high contrast, cyan → violet */
|
||||
.neon-bar {
|
||||
background-image: linear-gradient(
|
||||
100deg,
|
||||
hsl(var(--glow-1) / 0.95) 0%,
|
||||
hsl(var(--glow-2) / 0.95) 60%,
|
||||
hsl(var(--glow-1) / 0.95) 100%
|
||||
);
|
||||
box-shadow:
|
||||
0 0 8px 0 hsl(var(--glow-1) / 0.45),
|
||||
0 0 18px -2px hsl(var(--glow-2) / 0.4);
|
||||
}
|
||||
|
||||
/* Animated gradient background for loading states (Google-AI style flow) */
|
||||
.loading-gradient {
|
||||
background-image: linear-gradient(
|
||||
@@ -457,6 +470,26 @@
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Left neon indicator for the active sidebar item */
|
||||
.neon-sidebar-active {
|
||||
position: relative;
|
||||
}
|
||||
.neon-sidebar-active[data-active="true"]::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 20%;
|
||||
bottom: 20%;
|
||||
width: 3px;
|
||||
border-radius: 9999px;
|
||||
background-image: linear-gradient(
|
||||
180deg,
|
||||
hsl(var(--glow-1)),
|
||||
hsl(var(--glow-2))
|
||||
);
|
||||
box-shadow: 0 0 10px 1px hsl(var(--glow-1) / 0.6);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.loading-gradient { animation: none; }
|
||||
.loading-gradient-text { animation: none; }
|
||||
|
||||
Reference in New Issue
Block a user