refactor(theme): muted offset shadows over hard brutalist stamp
This commit is contained in:
+59
-13
@@ -69,17 +69,21 @@
|
|||||||
--space-20: 10rem;
|
--space-20: 10rem;
|
||||||
|
|
||||||
/* === BORDERS === */
|
/* === BORDERS === */
|
||||||
--border-width: 3px;
|
--border-width: 1.5px;
|
||||||
--radius: 0px;
|
--radius: 5px;
|
||||||
|
|
||||||
/* === BRUTALIST SHADOWS === */
|
/* Muted offset shadow — quieter than the old hard-blue brutalist stamp.
|
||||||
--shadow-brutal-xs: 1px 1px 0 var(--blue);
|
* Softer alpha + tighter offsets keep the offset-shadow feel, just lighter. */
|
||||||
--shadow-brutal-sm: 3px 3px 0 var(--blue);
|
--shadow-color: rgba(4, 28, 243, 0.25);
|
||||||
--shadow-brutal: 5px 5px 0 var(--blue);
|
|
||||||
--shadow-brutal-md: 7px 7px 0 var(--blue);
|
/* === OFFSET SHADOWS === */
|
||||||
--shadow-brutal-lg: 8px 8px 0 var(--blue);
|
--shadow-brutal-xs: 1px 1px 0 var(--shadow-color);
|
||||||
--shadow-brutal-xl: 10px 10px 0 var(--blue);
|
--shadow-brutal-sm: 2px 2px 0 var(--shadow-color);
|
||||||
--shadow-brutal-2xl: 12px 12px 0 var(--blue);
|
--shadow-brutal: 3px 3px 0 var(--shadow-color);
|
||||||
|
--shadow-brutal-md: 3px 3px 0 var(--shadow-color);
|
||||||
|
--shadow-brutal-lg: 4px 4px 0 var(--shadow-color);
|
||||||
|
--shadow-brutal-xl: 5px 5px 0 var(--shadow-color);
|
||||||
|
--shadow-brutal-2xl: 6px 6px 0 var(--shadow-color);
|
||||||
|
|
||||||
/* === GRID === */
|
/* === GRID === */
|
||||||
--grid-gap: var(--space-3);
|
--grid-gap: var(--space-3);
|
||||||
@@ -237,9 +241,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Button elevation transition — only transform animates; shadow snaps instantly */
|
/* Button elevation transition — transform + shadow animate together over 130ms
|
||||||
.btn-transition {
|
* on the micro easing curve; the spring feel comes from the curve, no keyframes. */
|
||||||
transition: transform 0.13s var(--ease-micro);
|
@utility btn-transition {
|
||||||
|
transition: transform 130ms var(--ease-micro);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Offset "shadow" as a static pseudo-block instead of box-shadow. box-shadow
|
||||||
|
* swims because it repaints on the main thread while the button's translate
|
||||||
|
* runs on the compositor — the two desync per frame. Here both the button and
|
||||||
|
* its ::before are transforms, so they stay frame-synced. The ::before counter-
|
||||||
|
* translates to hold a fixed 3px world offset while the button lifts/presses,
|
||||||
|
* so the shadow appears frozen and only the button moves. */
|
||||||
|
.btn-shadow {
|
||||||
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
transition: transform 130ms var(--ease-micro);
|
||||||
|
}
|
||||||
|
.btn-shadow::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
/* ponytail: inset:0 tracks the padding box, ~1.5px shy of the border box;
|
||||||
|
* imperceptible on a hard offset block. Widen with negative inset if it shows. */
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
border-radius: inherit;
|
||||||
|
background: var(--shadow-color);
|
||||||
|
transform: translate(3px, 3px);
|
||||||
|
transition: transform 130ms var(--ease-micro);
|
||||||
|
}
|
||||||
|
.btn-shadow:hover {
|
||||||
|
transform: translate(-2px, -2px);
|
||||||
|
}
|
||||||
|
.btn-shadow:hover::before {
|
||||||
|
transform: translate(5px, 5px);
|
||||||
|
}
|
||||||
|
.btn-shadow:active {
|
||||||
|
transform: translate(2px, 2px);
|
||||||
|
}
|
||||||
|
.btn-shadow:active::before {
|
||||||
|
transform: translate(1px, 1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Brutalist utility classes */
|
/* Brutalist utility classes */
|
||||||
@@ -254,6 +295,7 @@
|
|||||||
}
|
}
|
||||||
@utility brutal-border {
|
@utility brutal-border {
|
||||||
border: var(--border-width) solid var(--blue);
|
border: var(--border-width) solid var(--blue);
|
||||||
|
border-radius: var(--radius);
|
||||||
}
|
}
|
||||||
@utility brutal-border-top {
|
@utility brutal-border-top {
|
||||||
border-top: var(--border-width) solid var(--blue);
|
border-top: var(--border-width) solid var(--blue);
|
||||||
@@ -272,6 +314,7 @@
|
|||||||
* ancestor must not have overflow:hidden or the outline gets clipped. */
|
* ancestor must not have overflow:hidden or the outline gets clipped. */
|
||||||
@utility brutal-outline {
|
@utility brutal-outline {
|
||||||
outline: var(--border-width) solid var(--blue);
|
outline: var(--border-width) solid var(--blue);
|
||||||
|
border-radius: var(--radius);
|
||||||
}
|
}
|
||||||
/* Tiled blue dot pattern — applied to body::before (page-wide) and reusable
|
/* Tiled blue dot pattern — applied to body::before (page-wide) and reusable
|
||||||
* on any surface that should share the same paper-grain texture. The SVG
|
* on any surface that should share the same paper-grain texture. The SVG
|
||||||
@@ -479,6 +522,9 @@ dialog.lightbox {
|
|||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
max-width: calc(100vw - 2rem);
|
max-width: calc(100vw - 2rem);
|
||||||
max-height: calc(100vh - 10rem);
|
max-height: calc(100vh - 10rem);
|
||||||
|
/* Match the wrapper's brutal-outline radius so image corners sit flush
|
||||||
|
* inside the rounded outline instead of poking past it. */
|
||||||
|
border-radius: var(--radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disable transition animation for Firefox
|
/* Disable transition animation for Firefox
|
||||||
|
|||||||
@@ -41,14 +41,12 @@ function isAnchorProps(props: RestButton | RestAnchor): props is RestAnchor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const VARIANTS = {
|
const VARIANTS = {
|
||||||
primary:
|
primary: 'brutal-border bg-blue text-cream btn-shadow',
|
||||||
'brutal-border bg-blue text-cream shadow-[5px_5px_0_rgba(4,28,243,0.35)] hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-[7px_7px_0_rgba(4,28,243,0.35)] active:translate-x-0.5 active:translate-y-0.5 active:shadow-[1px_1px_0_rgba(4,28,243,0.35)]',
|
secondary: 'brutal-border bg-blue text-cream btn-shadow',
|
||||||
secondary:
|
|
||||||
'brutal-border bg-blue text-cream shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-md active:translate-x-0.5 active:translate-y-0.5 active:shadow-brutal-xs',
|
|
||||||
outline:
|
outline:
|
||||||
'brutal-border border-blue/35 bg-cream text-blue hover:border-blue hover:bg-blue/10 active:bg-blue active:text-cream',
|
'brutal-border border-blue/35 bg-cream text-blue hover:border-blue hover:bg-blue/10 active:bg-blue active:text-cream',
|
||||||
ghost:
|
ghost:
|
||||||
'brutal-border bg-transparent text-blue hover:-translate-x-0.5 hover:-translate-y-0.5 active:translate-x-0.5 active:translate-y-0.5',
|
'brutal-border bg-transparent text-blue btn-transition hover:-translate-x-0.5 hover:-translate-y-0.5 active:translate-x-0.5 active:translate-y-0.5',
|
||||||
} as const satisfies Record<ButtonVariant, string>;
|
} as const satisfies Record<ButtonVariant, string>;
|
||||||
|
|
||||||
const SIZES = {
|
const SIZES = {
|
||||||
@@ -57,9 +55,9 @@ const SIZES = {
|
|||||||
lg: 'px-8 py-4 text-lg',
|
lg: 'px-8 py-4 text-lg',
|
||||||
} as const satisfies Record<ButtonSize, string>;
|
} as const satisfies Record<ButtonSize, string>;
|
||||||
|
|
||||||
/* box-shadow excluded from transition intentionally — snaps instantly so the
|
/* Elevation lives per-variant: primary/secondary use btn-shadow (static offset
|
||||||
* eye follows the 130ms button movement, not the shadow change. */
|
* block, button moves), ghost uses btn-transition + translate. */
|
||||||
const BASE = 'cursor-pointer btn-transition uppercase tracking-wider';
|
const BASE = 'cursor-pointer uppercase tracking-wider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Brutalist button with variants and sizes.
|
* Brutalist button with variants and sizes.
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export function TechStackBrick({ name, className }: TechStackBrickProps) {
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'brutal-border brutal-shadow bg-cream px-4 py-3 text-center',
|
'brutal-border brutal-shadow bg-cream px-4 py-3 text-center',
|
||||||
'transition-all duration-200 hover:shadow-none hover:translate-x-[2px] hover:translate-y-[2px]',
|
'transition-all duration-200 hover:shadow-none hover:translate-x-[3px] hover:translate-y-[3px]',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user