Polish UI: Beautiful header, burger menu, logo, dialogs, and dropdowns inspired by GitHub Spark

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-19 22:14:04 +00:00
parent 1ef70d6661
commit 4b36e2c852
6 changed files with 569 additions and 59 deletions

View File

@@ -13,27 +13,7 @@ export function PageLayout({ children }: { children: ReactNode }) {
return (
<div className="min-h-screen bg-background">
<div
className="fixed inset-0 opacity-[0.03] pointer-events-none"
style={{
backgroundImage: `
repeating-linear-gradient(
0deg,
transparent,
transparent 40px,
oklch(0.75 0.18 200) 40px,
oklch(0.75 0.18 200) 41px
),
repeating-linear-gradient(
90deg,
transparent,
transparent 40px,
oklch(0.75 0.18 200) 40px,
oklch(0.75 0.18 200) 41px
)
`,
}}
/>
<div className="grid-pattern" />
<NavigationSidebar />
@@ -50,13 +30,13 @@ export function PageLayout({ children }: { children: ReactNode }) {
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.4 }}
className="flex items-center gap-3"
className="logo-container"
>
<Navigation />
<div className="h-10 w-10 rounded-lg bg-gradient-to-br from-primary to-accent flex items-center justify-center">
<Code className="h-5 w-5 text-primary-foreground" weight="bold" />
<div className="logo-icon-box">
<Code weight="bold" />
</div>
<h1 className="text-2xl font-bold tracking-tight bg-gradient-to-r from-primary via-accent to-primary bg-clip-text text-transparent">
<h1 className="logo-text">
CodeSnippet
</h1>
</motion.div>

View File

@@ -1,16 +1,15 @@
import { List } from '@phosphor-icons/react'
import { Button } from '@/components/ui/button'
import { useNavigation } from './useNavigation'
export function Navigation() {
const { menuOpen, setMenuOpen } = useNavigation()
return (
<Button
variant="ghost"
size="icon"
<button
className="nav-burger-btn"
onClick={() => setMenuOpen(!menuOpen)}
aria-label="Toggle navigation menu"
>
<List className="h-5 w-5" />
</Button>
<List weight="bold" />
</button>
)
}

View File

@@ -0,0 +1,332 @@
@use '../abstracts' as *;
// Dialog/Modal styling - beautiful and polished
[data-slot="dialog-overlay"],
.dialog-overlay {
position: fixed;
inset: 0;
z-index: 50;
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(8px);
animation: fadeIn 0.2s ease-out;
}
[data-slot="dialog"],
.dialog-content {
position: fixed;
left: 50%;
top: 50%;
z-index: 50;
transform: translate(-50%, -50%);
width: 90%;
max-width: 32rem;
max-height: 85vh;
overflow-y: auto;
background: linear-gradient(135deg, rgba($card, 0.95) 0%, rgba($card, 0.98) 100%);
border: 1px solid rgba($border, 0.5);
border-radius: $radius-xl;
box-shadow:
0 20px 40px rgba(0, 0, 0, 0.3),
0 10px 20px rgba(0, 0, 0, 0.2),
0 0 0 1px rgba($accent, 0.1);
animation: dialogSlideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&:focus {
outline: none;
}
}
// Dropdown menu styling - sleek and modern
[data-slot="dropdown-menu-content"],
.dropdown-content {
z-index: 50;
min-width: 12rem;
overflow: hidden;
border-radius: $radius-lg;
border: 1px solid rgba($border, 0.5);
background: rgba($popover, 0.95);
backdrop-filter: blur(12px) saturate(150%);
box-shadow:
0 10px 25px rgba(0, 0, 0, 0.2),
0 4px 10px rgba(0, 0, 0, 0.15),
0 0 0 1px rgba($accent, 0.05);
padding: 0.375rem;
animation: dropdownSlideIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);
&:focus {
outline: none;
}
}
[data-slot="dropdown-menu-item"],
.dropdown-item {
position: relative;
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
color: $foreground;
border-radius: $radius-sm;
cursor: pointer;
transition: all 0.15s ease;
user-select: none;
outline: none;
&:hover {
background-color: rgba($accent, 0.12);
color: $accent;
transform: translateX(2px);
}
&:focus {
background-color: rgba($accent, 0.15);
color: $accent;
}
&[data-disabled] {
pointer-events: none;
opacity: 0.5;
}
}
// Popover styling - refined and elegant
[data-slot="popover-content"],
.popover-content {
z-index: 50;
width: auto;
border-radius: $radius-lg;
border: 1px solid rgba($border, 0.5);
background: rgba($popover, 0.95);
backdrop-filter: blur(12px) saturate(150%);
padding: 1rem;
box-shadow:
0 10px 25px rgba(0, 0, 0, 0.2),
0 4px 10px rgba(0, 0, 0, 0.15);
animation: popoverSlideIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);
&:focus {
outline: none;
}
}
// Select dropdown styling - beautiful custom select
[data-slot="select-trigger"],
.select-trigger {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
border-radius: $radius-md;
border: 1px solid rgba($border, 0.5);
background: rgba($input, 0.5);
color: $foreground;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background: rgba($input, 0.7);
border-color: rgba($accent, 0.5);
}
&:focus {
outline: none;
border-color: $ring;
box-shadow: 0 0 0 3px rgba($ring, 0.1);
}
&[data-disabled] {
pointer-events: none;
opacity: 0.5;
}
}
[data-slot="select-content"],
.select-content {
z-index: 50;
min-width: 8rem;
overflow: hidden;
border-radius: $radius-lg;
border: 1px solid rgba($border, 0.5);
background: rgba($popover, 0.95);
backdrop-filter: blur(12px) saturate(150%);
box-shadow:
0 10px 25px rgba(0, 0, 0, 0.2),
0 4px 10px rgba(0, 0, 0, 0.15);
padding: 0.375rem;
animation: selectSlideIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
[data-slot="select-item"],
.select-item {
position: relative;
display: flex;
align-items: center;
padding: 0.5rem 0.75rem 0.5rem 2rem;
font-size: 0.875rem;
color: $foreground;
border-radius: $radius-sm;
cursor: pointer;
transition: all 0.15s ease;
outline: none;
user-select: none;
&:hover {
background-color: rgba($accent, 0.12);
color: $accent;
}
&:focus {
background-color: rgba($accent, 0.15);
color: $accent;
}
&[data-state="checked"] {
background-color: rgba($accent, 0.1);
color: $accent;
font-weight: 500;
&::before {
content: '';
position: absolute;
left: 0.5rem;
font-size: 0.875rem;
}
}
&[data-disabled] {
pointer-events: none;
opacity: 0.5;
}
}
// Alert Dialog styling - attention-grabbing
[data-slot="alert-dialog-content"],
.alert-dialog-content {
position: fixed;
left: 50%;
top: 50%;
z-index: 50;
transform: translate(-50%, -50%);
width: 90%;
max-width: 28rem;
background: linear-gradient(135deg, rgba($card, 0.95) 0%, rgba($card, 0.98) 100%);
border: 1px solid rgba($destructive, 0.3);
border-radius: $radius-xl;
box-shadow:
0 20px 40px rgba($destructive, 0.2),
0 10px 20px rgba(0, 0, 0, 0.2);
padding: 1.5rem;
animation: alertSlideIn 0.25s cubic-bezier(0.4, 0, 0.2, 1);
&:focus {
outline: none;
}
}
// Animations
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes dialogSlideIn {
from {
opacity: 0;
transform: translate(-50%, -48%) scale(0.96);
}
to {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
}
@keyframes dropdownSlideIn {
from {
opacity: 0;
transform: translateY(-8px) scale(0.96);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes popoverSlideIn {
from {
opacity: 0;
transform: scale(0.96);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes selectSlideIn {
from {
opacity: 0;
transform: translateY(-4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes alertSlideIn {
from {
opacity: 0;
transform: translate(-50%, -45%) scale(0.9);
}
to {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
}
// Namespace selector specific styling
.namespace-select {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
font-weight: 500;
border-radius: $radius-lg;
border: 1px solid rgba($border, 0.5);
background: rgba($secondary, 0.5);
color: $foreground;
cursor: pointer;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
svg {
width: 1rem;
height: 1rem;
color: $muted-foreground;
transition: color 0.2s ease;
}
&:hover {
background: rgba($secondary, 0.7);
border-color: rgba($accent, 0.4);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba($accent, 0.1);
svg {
color: $accent;
}
}
&:focus {
outline: none;
border-color: $ring;
box-shadow: 0 0 0 3px rgba($ring, 0.1);
}
}

View File

@@ -0,0 +1,219 @@
@use '../abstracts' as *;
// Header and navigation specific styles - GitHub Spark inspired
header {
border-bottom: 1px solid rgba($border, 0.5);
background-color: rgba($background, 0.8);
backdrop-filter: blur(16px) saturate(180%);
position: sticky;
top: 0;
z-index: 20;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
.container {
padding: 1rem 1.5rem;
}
}
// Burger menu / navigation button styling - refined and polished
.nav-burger-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2.25rem;
height: 2.25rem;
padding: 0.375rem;
border-radius: $radius-md;
background-color: rgba($secondary, 0.5);
border: 1px solid rgba($border, 0.5);
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
position: relative;
svg {
width: 1.25rem;
height: 1.25rem;
color: $foreground;
transition: all 0.2s ease;
}
&::before {
content: '';
position: absolute;
inset: -2px;
border-radius: $radius-md;
padding: 2px;
background: linear-gradient(135deg, $accent 0%, transparent 100%);
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
opacity: 0;
transition: opacity 0.2s ease;
}
&:hover {
background-color: rgba($accent, 0.15);
border-color: rgba($accent, 0.4);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba($accent, 0.15);
&::before {
opacity: 1;
}
svg {
color: $accent;
transform: scale(1.05);
}
}
&:active {
transform: translateY(0);
}
&:focus-visible {
outline: 2px solid $ring;
outline-offset: 2px;
}
}
// Logo styling improvements - elegant and refined
.logo-container {
display: flex;
align-items: center;
gap: 0.875rem;
}
.logo-icon-box {
width: 2.25rem;
height: 2.25rem;
border-radius: $radius-lg;
background: linear-gradient(135deg, $primary 0%, hsl(195, 100%, 60%) 100%);
display: flex;
align-items: center;
justify-content: center;
box-shadow:
0 2px 8px rgba($primary, 0.25),
0 1px 2px rgba(0, 0, 0, 0.1),
inset 0 1px 1px rgba(255, 255, 255, 0.1);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
45deg,
transparent,
rgba(255, 255, 255, 0.1),
transparent
);
transform: rotate(45deg);
transition: all 0.5s ease;
}
svg {
width: 1.25rem;
height: 1.25rem;
color: $primary-foreground;
position: relative;
z-index: 1;
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2));
}
&:hover {
transform: translateY(-2px) scale(1.05);
box-shadow:
0 6px 20px rgba($primary, 0.35),
0 2px 4px rgba(0, 0, 0, 0.15),
inset 0 1px 1px rgba(255, 255, 255, 0.15);
&::before {
left: 100%;
}
}
}
.logo-text {
font-size: 1.375rem;
font-weight: 700;
letter-spacing: -0.02em;
background: linear-gradient(135deg, $primary 0%, $accent 50%, $primary 100%);
background-size: 200% auto;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: gradient-shift 4s ease infinite;
position: relative;
text-shadow: 0 0 30px rgba($accent, 0.3);
&::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, $accent, transparent);
opacity: 0;
transition: opacity 0.3s ease;
}
&:hover::after {
opacity: 0.6;
}
}
@keyframes gradient-shift {
0%, 100% {
background-position: 0% center;
}
50% {
background-position: 200% center;
}
}
// Grid pattern adjustment - ultra subtle like GitHub Spark
.grid-pattern {
position: fixed;
inset: 0;
opacity: 0.015; // Very subtle
pointer-events: none;
background-image:
repeating-linear-gradient(
0deg,
transparent,
transparent 48px,
rgba($accent, 0.15) 48px,
rgba($accent, 0.15) 49px
),
repeating-linear-gradient(
90deg,
transparent,
transparent 48px,
rgba($accent, 0.15) 48px,
rgba($accent, 0.15) 49px
);
background-size: 48px 48px;
}
// Header animations
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
header {
animation: fadeInDown 0.4s ease-out;
}

View File

@@ -4,3 +4,5 @@
@forward './cards';
@forward './forms';
@forward './typography';
@forward './header';
@forward './dialogs';

View File

@@ -260,47 +260,25 @@ $space-values: (2, 3, 4, 6, 8);
text-underline-offset: 4px !important;
}
// Gap variations (additional to the ones from spacing loop)
.gap-1 { gap: 0.25rem !important; }
// Gap variations - only adding those NOT in spacing map
.gap-1\.5 { gap: 0.375rem !important; }
.gap-2 { gap: 0.5rem !important; }
.gap-3 { gap: 0.75rem !important; }
.gap-4 { gap: 1rem !important; }
.gap-6 { gap: 1.5rem !important; }
.gap-8 { gap: 2rem !important; }
.gap-12 { gap: 3rem !important; }
// Rounded variations (additional)
.rounded-xs { border-radius: 0.125rem !important; }
.rounded-full { border-radius: 9999px !important; }
// Padding variations (additional single values not in spacing map)
.px-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; }
.px-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; }
.px-3 { padding-left: 0.75rem !important; padding-right: 0.75rem !important; }
.px-4 { padding-left: 1rem !important; padding-right: 1rem !important; }
.px-6 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; }
// Padding variations - only py-0 and py-20 which are not in the spacing map
.py-0 { padding-top: 0 !important; padding-bottom: 0 !important; }
.py-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; }
.py-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; }
.py-4 { padding-top: 1rem !important; padding-bottom: 1rem !important; }
.py-6 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; }
.py-8 { padding-top: 2rem !important; padding-bottom: 2rem !important; }
.py-20 { padding-top: 5rem !important; padding-bottom: 5rem !important; }
// Space between variations (additional)
.space-x-2 > * + * { margin-left: 0.5rem !important; }
.space-x-3 > * + * { margin-left: 0.75rem !important; }
// Space between variations - only adding those NOT in the loop
.space-y-0 > * + * { margin-top: 0 !important; }
.space-y-1 > * + * { margin-top: 0.25rem !important; }
.space-y-16 > * + * { margin-top: 4rem !important; }
// SVG sizing helper - using attribute selector instead of complex class name
svg {
&[class*="h-5"] { height: 1.25rem; }
&[class*="w-5"] { width: 1.25rem; }
}
// SVG sizing - explicit class matches for h-5 and w-5
svg.h-5 { height: 1.25rem !important; }
svg.w-5 { width: 1.25rem !important; }
// Button-specific utilities using nesting
button {