Skip to content

Commit

Permalink
feat: native buttons with icons
Browse files Browse the repository at this point in the history
  • Loading branch information
maxholman committed Nov 2, 2022
1 parent 13c2c62 commit 067bc9b
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 53 deletions.
25 changes: 23 additions & 2 deletions lib/buttons.css.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import { createVar, style, styleVariants } from '@vanilla-extract/css';
import { calc } from '@vanilla-extract/css-utils';
import { colorVariantVars, genericVars, rotate } from './theme.css.js';

export type ButtonVariant = 'standard' | 'ghost' | 'subtle' | 'transparent';

export const iconClass = style({
display: 'inline-flex',
height: '1em',
aspectRatio: '1/1',
alignItems: 'center',
justifySelf: 'center',
});

const base = style({
cursor: 'pointer',
borderStyle: 'solid',
display: 'flex',
borderWidth: genericVars.border.weight.normal,
borderRadius: genericVars.radius.standard,
flexDirection: 'row',
alignItems: 'center',
padding: `${genericVars.space.small} ${genericVars.space.standard}`,
textAlign: 'center',
fontSize: genericVars.text.size.normal,
Expand All @@ -32,6 +44,10 @@ export const compactButton = style({
fontSize: genericVars.text.size.small,
});

export const iconButtonClass = style({
gap: `0 0.5em`,
});

const buttonColorVar = createVar();

const variants: Record<
Expand Down Expand Up @@ -94,7 +110,7 @@ export const buttonVariantClasses = styleVariants(variants, (variant) => [

// WARN: this is defined last so it can override other styles
// with the same specificity
export const busyButton = style({
export const busyButtonClass = style({
color: 'transparent',
position: 'relative',
display: 'grid',
Expand All @@ -104,7 +120,7 @@ export const busyButton = style({
// borderColor: 'revert',
// },
'&::before': {
height: '60%',
height: '1em',
aspectRatio: '1/1',
content: '""',
position: 'absolute',
Expand All @@ -121,3 +137,8 @@ export const busyButton = style({
},
},
});

export const inlineBleedClass = style({
marginTop: calc(genericVars.space.small).negate().toString(),
marginBottom: calc(`${genericVars.space.small}`).negate().toString(),
});
71 changes: 45 additions & 26 deletions lib/buttons.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,72 @@
import { ClassValue, clsx } from 'clsx';
import type { ButtonHTMLAttributes, FC, PropsWithChildren } from 'react';
import type { Merge } from 'type-fest';
import type { FC, ReactElement } from 'react';
import {
busyButton,
compactButton,
busyButtonClass,
ButtonVariant,
buttonVariantClasses,
compactButton,
iconButtonClass,
iconClass,
inlineBleedClass,
} from './buttons.css.js';
import { Align, inlineAlignSelf } from './layout.css.js';
import { Box, BoxBasedComponentProps } from './core.js';
import type { Align } from './layout.css.js';

export type ButtonProps = {
export type ButtonCommonProps = {
className?: ClassValue;
variant?: ButtonVariant;
busy?: boolean;
compact?: boolean;
className?: ClassValue;
align?: Align;
inline?: boolean;
};

export type ButtonProps = ButtonCommonProps & {
icon?: ReactElement;
};

export type ButtonIconProps = ButtonCommonProps & {};

export const Button: FC<
PropsWithChildren<
Merge<
ButtonHTMLAttributes<HTMLButtonElement>,
{
variant?: ButtonVariant;
busy?: boolean;
compact?: boolean;
className?: ClassValue;
align?: Align;
}
>
>
BoxBasedComponentProps<'button' | 'a' | 'span', ButtonProps>
> = ({
component = 'button',
variant = 'standard',
type = 'button',
compact,
busy,
align,
className,
icon,
inline,
children,
...props
}) => (
<button
<Box
component={component}
className={clsx(
busy && busyButton,
buttonVariantClasses[variant],
className,
busy && busyButtonClass,
buttonVariantClasses[variant],
compact && compactButton,
align && inlineAlignSelf[align],
icon && iconButtonClass,
inline && inlineBleedClass,
)}
type={type}
{...props}
/>
// type={component === 'button' && !props.type ? 'button' : undefined}
>
{icon && <span className={iconClass}>{icon}</span>}
{children}
</Box>
);

export const ButtonLink: FC<
BoxBasedComponentProps<'span' | 'a', ButtonProps>
> = ({ variant = 'standard', compact, busy, align, className, ...props }) => (
<Button component={'href' in props ? 'a' : 'span'} {...props}></Button>
);

export const ButtonIcon: FC<
BoxBasedComponentProps<'button', ButtonIconProps>
> = ({ children, ...props }) => (
<Button {...props}>{<span className={iconClass}>{children}</span>}</Button>
);
24 changes: 0 additions & 24 deletions lib/links.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import { clsx } from 'clsx';
import type { FC } from 'react';
import {
busyButton,
buttonVariantClasses,
compactButton,
} from './buttons.css.js';
import type { ButtonProps } from './buttons.js';
import { Box, BoxBasedComponentProps } from './core.js';
import { inlineAlignSelf } from './layout.css.js';
import { linkStyle } from './links.css.js';

export const TextLink: FC<BoxBasedComponentProps<'span' | 'a'>> = ({
Expand All @@ -24,19 +16,3 @@ export const TextLink: FC<BoxBasedComponentProps<'span' | 'a'>> = ({
{children}
</Box>
);

export const ButtonLink: FC<
BoxBasedComponentProps<'span' | 'a', ButtonProps>
> = ({ variant = 'standard', compact, busy, align, className, ...props }) => (
<Box
component={'href' in props ? 'a' : 'span'}
className={clsx(
busy && busyButton,
buttonVariantClasses[variant],
className,
compact && compactButton,
align && inlineAlignSelf[align],
)}
{...props}
/>
);
25 changes: 24 additions & 1 deletion src/buttons/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
import type { FC } from 'react';
import { Button } from '../../lib/buttons.js';
import { Block, Inline } from '../../lib/layout.js';
import { ButtonLink } from '../../lib/links.js';
import { ButtonLink } from '../../lib/buttons.js';
import { Panel } from '../../lib/panel.js';
import { Heading, Text } from '../../lib/typography.js';
import { SunIcon, CrescentMoonIcon } from '../icons.js';

export const ButtonsPage: FC = () => (
<Panel variant="ghost" space="huge">
<Block>
<Heading level="2">Buttons</Heading>
<Inline>
<Button>Button</Button>
<Button icon={<CrescentMoonIcon />}>Button</Button>
<Button variant="ghost">Ghost</Button>
<Button icon={<CrescentMoonIcon />} variant="ghost">
Ghost
</Button>
<Button variant="subtle">Subtle</Button>
<Button icon={<CrescentMoonIcon />} variant="subtle">
Subtle
</Button>
</Inline>
</Block>

Expand All @@ -25,6 +33,21 @@ export const ButtonsPage: FC = () => (
</Inline>
</Block>

<Block>
<Heading level="2">Button Icons</Heading>
<Inline>
<ButtonLink>
<SunIcon />
</ButtonLink>
<ButtonLink variant="ghost">
<SunIcon />
</ButtonLink>
<ButtonLink variant="subtle">
<SunIcon />
</ButtonLink>
</Inline>
</Block>

<Block>
<Heading level="2">Busy Buttons</Heading>
<Text>These buttons are super busy</Text>
Expand Down

0 comments on commit 067bc9b

Please sign in to comment.