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