Fix/css tweaks and bugs #11
@@ -56,7 +56,7 @@ export function ProjectCard({ title, year, description, tags, url, imageUrl, pri
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<Button href={url} variant="primary" size="sm" className="self-start lg:w-full lg:self-auto text-center">
|
||||
<Button href={url} variant="solid" size="sm" className="self-start lg:w-full lg:self-auto text-center">
|
||||
View Project
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -13,11 +13,8 @@ type Story = StoryObj<typeof Button>;
|
||||
export const AllVariants: Story = {
|
||||
render: () => (
|
||||
<div className="flex gap-4 flex-wrap p-8 bg-ochre-clay">
|
||||
<Button variant="primary" size="md">
|
||||
Primary
|
||||
</Button>
|
||||
<Button variant="secondary" size="md">
|
||||
Secondary
|
||||
<Button variant="solid" size="md">
|
||||
Solid
|
||||
</Button>
|
||||
<Button variant="outline" size="md">
|
||||
Outline
|
||||
@@ -32,13 +29,13 @@ export const AllVariants: Story = {
|
||||
export const Sizes: Story = {
|
||||
render: () => (
|
||||
<div className="flex gap-4 items-center flex-wrap p-8 bg-ochre-clay">
|
||||
<Button variant="primary" size="sm">
|
||||
<Button variant="solid" size="sm">
|
||||
Small
|
||||
</Button>
|
||||
<Button variant="primary" size="md">
|
||||
<Button variant="solid" size="md">
|
||||
Medium
|
||||
</Button>
|
||||
<Button variant="primary" size="lg">
|
||||
<Button variant="solid" size="lg">
|
||||
Large
|
||||
</Button>
|
||||
</div>
|
||||
@@ -47,7 +44,7 @@ export const Sizes: Story = {
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
variant: 'primary',
|
||||
variant: 'solid',
|
||||
disabled: true,
|
||||
children: 'Disabled',
|
||||
},
|
||||
|
||||
@@ -14,14 +14,10 @@ describe('Button', () => {
|
||||
});
|
||||
});
|
||||
describe('variants', () => {
|
||||
it('applies primary variant by default', () => {
|
||||
it('applies solid variant by default', () => {
|
||||
render(<Button>Go</Button>);
|
||||
expect(screen.getByRole('button')).toHaveClass('bg-blue');
|
||||
});
|
||||
it('applies secondary variant', () => {
|
||||
render(<Button variant="secondary">Go</Button>);
|
||||
expect(screen.getByRole('button')).toHaveClass('bg-blue');
|
||||
});
|
||||
it('applies outline variant', () => {
|
||||
render(<Button variant="outline">Go</Button>);
|
||||
expect(screen.getByRole('button')).toHaveClass('bg-cream');
|
||||
@@ -82,7 +78,7 @@ describe('Button', () => {
|
||||
});
|
||||
it('applies the same variant and size classes as button', () => {
|
||||
render(
|
||||
<Button href="/test" variant="primary" size="sm">
|
||||
<Button href="/test" variant="solid" size="sm">
|
||||
Go
|
||||
</Button>,
|
||||
);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { AnchorHTMLAttributes, ButtonHTMLAttributes, ReactNode } from 'react';
|
||||
import { cn } from '$shared/lib';
|
||||
|
||||
export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost';
|
||||
export type ButtonVariant = 'solid' | 'outline' | 'ghost';
|
||||
export type ButtonSize = 'sm' | 'md' | 'lg';
|
||||
|
||||
type BaseProps = {
|
||||
/**
|
||||
* Visual variant
|
||||
* @default 'primary'
|
||||
* @default 'solid'
|
||||
*/
|
||||
variant?: ButtonVariant;
|
||||
/**
|
||||
@@ -41,10 +41,9 @@ function isAnchorProps(props: RestButton | RestAnchor): props is RestAnchor {
|
||||
}
|
||||
|
||||
const VARIANTS = {
|
||||
primary: 'brutal-border bg-blue text-cream btn-shadow',
|
||||
secondary: 'brutal-border bg-blue text-cream btn-shadow',
|
||||
outline:
|
||||
'brutal-border border-blue/35 bg-cream text-blue hover:border-blue hover:bg-blue/10 active:bg-blue active:text-cream',
|
||||
solid: 'brutal-border bg-blue text-cream btn-shadow',
|
||||
// Reversed solid: same border + offset-block animation, cream fill instead of blue
|
||||
outline: 'brutal-border bg-cream text-blue btn-shadow',
|
||||
ghost:
|
||||
'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>;
|
||||
@@ -55,15 +54,15 @@ const SIZES = {
|
||||
lg: 'px-8 py-4 text-lg',
|
||||
} as const satisfies Record<ButtonSize, string>;
|
||||
|
||||
/* Elevation lives per-variant: primary/secondary use btn-shadow (static offset
|
||||
* block, button moves), ghost uses btn-transition + translate. */
|
||||
/* Elevation lives per-variant: solid uses btn-shadow (static offset block,
|
||||
* button moves), ghost uses btn-transition + translate. */
|
||||
const BASE = 'cursor-pointer uppercase tracking-wider';
|
||||
|
||||
/**
|
||||
* Brutalist button with variants and sizes.
|
||||
* Renders as <a> when href is provided, <button> otherwise.
|
||||
*/
|
||||
export function Button({ variant = 'primary', size = 'md', className, children, ...props }: Props) {
|
||||
export function Button({ variant = 'solid', size = 'md', className, children, ...props }: Props) {
|
||||
const cls = cn(BASE, VARIANTS[variant], SIZES[size], className);
|
||||
|
||||
if (isAnchorProps(props)) {
|
||||
|
||||
Reference in New Issue
Block a user