Last active
February 15, 2022 11:31
-
-
Save DouglasdeMoura/4562feb9fc5f0baaae5741ab3fbe6acf to your computer and use it in GitHub Desktop.
Simple React Input validation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { render, screen } from '@testing-library/react' | |
import userEvent from '@testing-library/user-event' | |
import { Input } from '.' | |
describe('<Input />', () => { | |
it('should render the component', () => { | |
render(<Input label="mock_label" />) | |
expect(screen.getByLabelText('mock_label')).toBeInTheDocument() | |
}) | |
it('should validate a required input', () => { | |
const handleOnBlur = jest.fn() | |
render( | |
<Input | |
label="mock_label" | |
error="This input is required" | |
onBlur={handleOnBlur} | |
required | |
/>, | |
) | |
userEvent.tab() | |
userEvent.tab() | |
expect(handleOnBlur).toHaveBeenCalledTimes(1) | |
expect(screen.getByText('This input is required')).toBeInTheDocument() | |
}) | |
it('should validate an input with a defined minLength', () => { | |
const handleOnBlur = jest.fn() | |
render( | |
<Input | |
label="mock_label" | |
error="The minLength is 5" | |
onBlur={handleOnBlur} | |
minLength={5} | |
/>, | |
) | |
userEvent.type(screen.getByLabelText('mock_label'), '123') | |
userEvent.tab() | |
expect(handleOnBlur).toHaveBeenCalledTimes(1) | |
expect(screen.getByText('The minLength is 5')).toBeInTheDocument() | |
}) | |
it('should validate an input with a defined maxLength', () => { | |
const handleOnBlur = jest.fn() | |
render( | |
<Input | |
label="mock_label" | |
error="The maxLength is 5" | |
defaultValue="1234567" | |
onBlur={handleOnBlur} | |
maxLength={5} | |
/>, | |
) | |
const input = screen.getByLabelText('mock_label') as HTMLInputElement | |
userEvent.type(input, '{backspace}') | |
userEvent.tab() | |
expect(handleOnBlur).toHaveBeenCalledTimes(1) | |
expect(screen.getByText('The maxLength is 5')).toBeInTheDocument() | |
}) | |
it('should validate a pattern', () => { | |
const handleOnBlur = jest.fn() | |
render( | |
<Input | |
label="mock_label" | |
error="You must enter three numbers" | |
onBlur={handleOnBlur} | |
pattern={'[0-9]{3}'} | |
/>, | |
) | |
const input = screen.getByLabelText('mock_label') as HTMLInputElement | |
userEvent.type(input, '1234567') | |
userEvent.tab() | |
expect(handleOnBlur).toHaveBeenCalledTimes(1) | |
expect(screen.getByText('You must enter three numbers')).toBeInTheDocument() | |
}) | |
it('should validate a min and max range', () => { | |
const handleOnBlur = jest.fn() | |
render( | |
<Input | |
label="mock_label" | |
error="You must enter a number between 3 and 5" | |
onBlur={handleOnBlur} | |
min={3} | |
max={5} | |
/>, | |
) | |
userEvent.type(screen.getByLabelText('mock_label'), '1') | |
userEvent.tab() | |
expect(handleOnBlur).toHaveBeenCalledTimes(1) | |
expect( | |
screen.getByText('You must enter a number between 3 and 5'), | |
).toBeInTheDocument() | |
userEvent.type(screen.getByLabelText('mock_label'), '5') | |
userEvent.tab() | |
expect( | |
screen.getByText('You must enter a number between 3 and 5'), | |
).toBeInTheDocument() | |
}) | |
it('should call onError when input is invalid and onValidate when the input is valid', () => { | |
const onValidate = jest.fn() | |
const onError = jest.fn() | |
render( | |
<Input | |
label="mock_label" | |
error="You must enter an email" | |
onValidate={onValidate} | |
onError={onError} | |
type="email" | |
/>, | |
) | |
userEvent.type(screen.getByLabelText('mock_label'), 'test@') | |
userEvent.tab() | |
expect(onError).toHaveBeenCalledWith('test@') | |
userEvent.type(screen.getByLabelText('mock_label'), 'example.com') | |
userEvent.tab() | |
expect(onValidate).toHaveBeenCalledWith('test@example.com') | |
}) | |
it('should display custom error messages', () => { | |
render( | |
<Input | |
label="mock_label" | |
error={{ | |
min: '>3', | |
max: '<5', | |
}} | |
min={3} | |
max={5} | |
/>, | |
) | |
userEvent.type(screen.getByLabelText('mock_label'), '1') | |
userEvent.tab() | |
expect(screen.getByText('>3')).toBeInTheDocument() | |
userEvent.type(screen.getByLabelText('mock_label'), '14') | |
userEvent.tab() | |
expect(screen.getByText('<5')).toBeInTheDocument() | |
}) | |
it('should display the search icon', () => { | |
render(<Input label="mock_label" type="search" />) | |
expect(screen.getByTestId('button-search-icon')).toBeInTheDocument() | |
}) | |
it('should display the eye icon', async () => { | |
render(<Input label="mock_label" type="password" />) | |
expect(screen.getByTestId('eye-off-icon')).toBeInTheDocument() | |
userEvent.click(screen.getByTestId('button-eye-icon')) | |
expect(screen.getByLabelText('mock_label')).toHaveAttribute('type', 'text') | |
expect(screen.getByTestId('eye-empty-icon')).toBeInTheDocument() | |
}) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ComponentMeta, ComponentStory } from '@storybook/react' | |
import { Input } from '.' | |
export default { | |
title: 'UI/Input', | |
component: Input, | |
args: { | |
label: 'Name', | |
placeholder: 'Enter your name', | |
}, | |
} as ComponentMeta<typeof Input> | |
const Template: ComponentStory<typeof Input> = (args) => <Input {...args} /> | |
export const Default = Template.bind({}) | |
export const Invalid = Template.bind({}) | |
Invalid.args = { | |
label: 'E-mail', | |
placeholder: 'Enter your e-mail', | |
defaultValue: '1234', | |
type: 'email', | |
error: 'Invalid e-mail', | |
} | |
export const Search = Template.bind({}) | |
Search.args = { | |
label: 'Search', | |
placeholder: 'Type your search', | |
type: 'search', | |
} | |
export const Password = Template.bind({}) | |
Password.args = { | |
label: 'Password', | |
placeholder: 'Enter your password', | |
type: 'password', | |
defaultValue: 'Pa$sw0rd', | |
} | |
export const Disabled = Template.bind({}) | |
Disabled.args = { | |
label: 'Disabled', | |
placeholder: 'Type your search', | |
disabled: true, | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { styled } from '~/stitches.config' | |
export const Wrapper = styled('div', { | |
display: 'flex', | |
flexDirection: 'column', | |
gap: '$4', | |
fontFamily: '$sans', | |
lineHeight: '1', | |
position: 'relative', | |
paddingBottom: '$16', | |
marginBottom: '$16', | |
width: '100%', | |
}) | |
export const Label = styled('label', { | |
color: '$gray900', | |
fontSize: '$14', | |
fontWeight: '$bold', | |
'&[data-disabled="true"]': { | |
color: '$gray400', | |
cursor: 'not-allowed', | |
}, | |
'&[data-invalid="true"]': { | |
color: '$red500', | |
}, | |
}) | |
export const InputContainer = styled('div', { | |
position: 'relative', | |
boxSizing: 'border-box', | |
}) | |
export const Input = styled('input', { | |
borderTop: 'none', | |
borderLeft: 'none', | |
borderRight: 'none', | |
borderBottom: '1px solid $gray900', | |
fontFamily: '$sans', | |
fontWeight: '$normal', | |
fontSize: '$16', | |
padding: '$14 0', | |
width: '100%', | |
boxSizing: 'border-box', | |
transition: 'border-color $easeInOut', | |
'&:focus-visible': { | |
outline: 'none', | |
borderColor: '$purple400', | |
}, | |
'&[data-invalid="true"]': { | |
borderColor: '$red500', | |
}, | |
'&[type="password"], &[type="search"]': { | |
paddingRight: '$16', | |
}, | |
'&:disabled': { | |
borderColor: '$gray400', | |
color: '$gray400', | |
cursor: 'not-allowed', | |
}, | |
}) | |
export const Icon = styled('button', { | |
position: 'absolute', | |
top: '50%', | |
right: '0', | |
transform: 'translateY(-50%)', | |
background: 'none', | |
border: 'none', | |
outline: 'none', | |
cursor: 'pointer', | |
padding: '0', | |
height: '47px', | |
'&:focus-visible': { | |
outline: 'none', | |
}, | |
}) | |
export const Error = styled('p', { | |
fontSize: '$12', | |
margin: '0', | |
color: '$red500', | |
fontWeight: '$normal', | |
position: 'absolute', | |
bottom: '0', | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
FocusEvent, | |
forwardRef, | |
useState, | |
useRef, | |
ChangeEvent, | |
RefObject, | |
useEffect, | |
} from 'react' | |
import mergeRefs from 'react-merge-refs' | |
import { EyeEmpty, EyeOff, Search } from 'iconoir-react' | |
import { kebabCase } from 'lodash' | |
import * as S from './input.styles' | |
// Fix typing error in Stitches (it should be unecessary on the next release). | |
type StyledComponentProps< | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
C extends keyof JSX.IntrinsicElements | React.JSXElementConstructor<any>, | |
> = Omit<React.ComponentProps<C>, 'css'> | |
type ErrorCategories = { | |
type?: string | |
minLength?: string | |
maxLength?: string | |
min?: string | |
max?: string | |
pattern?: string | |
required?: string | |
custom?: string | |
} | |
type Error = boolean | string | ErrorCategories | |
type InputProps = { | |
label: string | |
error?: Error | |
onError?: (value?: string) => void | |
onValidate?: (value?: string) => void | |
} & StyledComponentProps<typeof S.Input> | |
function validate( | |
props: Pick< | |
InputProps, | |
'required' | 'minLength' | 'maxLength' | 'pattern' | 'type' | 'min' | 'max' | |
>, | |
element: RefObject<HTMLInputElement>, | |
): { valid: boolean; error?: keyof ErrorCategories } { | |
if (element.current?.validity.valueMissing) { | |
return { | |
valid: false, | |
error: 'required', | |
} | |
} | |
if ( | |
element.current?.validity.tooShort || | |
(props?.minLength && element!.current!.value.length < props.minLength) | |
) { | |
return { | |
valid: false, | |
error: 'minLength', | |
} | |
} | |
if ( | |
element.current?.validity.tooLong || | |
(props?.maxLength && element!.current!.value.length > props.maxLength) | |
) { | |
return { | |
valid: false, | |
error: 'maxLength', | |
} | |
} | |
if (element.current?.validity.patternMismatch) { | |
return { | |
valid: false, | |
error: 'pattern', | |
} | |
} | |
if (element.current?.validity.typeMismatch) { | |
return { | |
valid: false, | |
error: 'type', | |
} | |
} | |
if ( | |
element.current?.validity.rangeUnderflow || | |
(props?.min && Number(element!.current!.value) < props.min) | |
) { | |
return { | |
valid: false, | |
error: 'min', | |
} | |
} | |
if ( | |
element.current?.validity.rangeOverflow || | |
(props?.max && Number(element!.current!.value) > props.max) | |
) { | |
return { | |
valid: false, | |
error: 'max', | |
} | |
} | |
return { | |
valid: true, | |
error: undefined, | |
} | |
} | |
export const Input = forwardRef<HTMLInputElement, InputProps>( | |
( | |
{ label, error, disabled, onBlur, onChange, onError, onValidate, ...props }, | |
ref, | |
) => { | |
const inputRef = useRef<HTMLInputElement>(null) | |
const [showPassword, setShowPassword] = useState(false) | |
const [hasError, setHasError] = useState(false) | |
const [bailedAt, setBailedAt] = useState< | |
keyof ErrorCategories | undefined | |
>() | |
const id = props.id || kebabCase(label) | |
const name = props.name || id | |
const type = showPassword ? 'text' : props.type || 'text' | |
const handleFocusEvent = (e: FocusEvent<HTMLInputElement>) => { | |
onBlur?.(e) | |
const validation = validate(props, inputRef) | |
if (!validation.valid) { | |
setHasError(true) | |
setBailedAt(validation.error) | |
return | |
} | |
if (!onValidate?.(inputRef.current?.value)) { | |
setHasError(true) | |
setBailedAt('custom') | |
} | |
} | |
const handleOnChange = (e: ChangeEvent<HTMLInputElement>) => { | |
onChange?.(e) | |
if (hasError) { | |
setHasError(false) | |
setBailedAt(undefined) | |
} | |
} | |
useEffect(() => { | |
if (hasError && typeof onError === 'function') { | |
onError?.(inputRef.current?.value) | |
} | |
}, [hasError, onError]) | |
return ( | |
<S.Wrapper data-disabled={disabled}> | |
<S.Label htmlFor={id} data-disabled={disabled} data-invalid={hasError}> | |
{label} | |
</S.Label> | |
<S.InputContainer> | |
<S.Input | |
{...props} | |
data-invalid={hasError} | |
disabled={disabled} | |
id={id} | |
name={name} | |
type={type} | |
onBlur={handleFocusEvent} | |
onChange={handleOnChange} | |
ref={mergeRefs([inputRef, ref])} | |
/> | |
{(type === 'password' || showPassword) && ( | |
<S.Icon | |
data-testid="button-eye-icon" | |
type="button" | |
onClick={() => setShowPassword(!showPassword)} | |
> | |
{showPassword ? ( | |
<EyeEmpty data-testid="eye-empty-icon" /> | |
) : ( | |
<EyeOff data-testid="eye-off-icon" /> | |
)} | |
</S.Icon> | |
)} | |
{type === 'search' && ( | |
<S.Icon data-testid="button-search-icon"> | |
<Search /> | |
</S.Icon> | |
)} | |
</S.InputContainer> | |
{hasError && error && ( | |
<S.Error> | |
{bailedAt && typeof error === 'object' ? error[bailedAt] : error} | |
</S.Error> | |
)} | |
</S.Wrapper> | |
) | |
}, | |
) | |
Input.displayName = 'Input' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createStitches } from '@stitches/react' | |
const { | |
styled, | |
css, | |
globalCss, | |
keyframes, | |
getCssText, | |
theme, | |
createTheme, | |
config, | |
} = createStitches({ | |
media: { | |
mobile: '(min-width: 576px)', | |
tablet: '(min-width: 768px)', | |
desktop: '(min-width: 1024px)', | |
widescreen: '(min-width: 1280px)', | |
fullHd: '(min-width: 1440px)', | |
}, | |
theme: { | |
colors: { | |
slate50: '#F8FAFC', | |
slate100: '#F1F5F9', | |
slate200: '#E2E8F0', | |
slate300: '#CBD5E1', | |
slate400: '#94A3B8', | |
slate500: '#64748B', | |
slate600: '#475569', | |
slate700: '#334155', | |
slate800: '#1E293B', | |
slate900: '#0F172A', | |
gray50: '#F9FAFB', | |
gray100: '#F3F4F6', | |
gray200: '#E5E7EB', | |
gray300: '#D1D5DB', | |
gray400: '#9CA3AF', | |
gray500: '#6B7280', | |
gray600: '#4B5563', | |
gray700: '#374151', | |
gray800: '#1F2937', | |
gray900: '#111827', | |
zinc50: '#FAFAFA', | |
zinc100: '#F4F4F5', | |
zinc200: '#E4E4E7', | |
zinc300: '#D4D4D8', | |
zinc400: '#A1A1AA', | |
zinc500: '#71717A', | |
zinc600: '#52525B', | |
zinc700: '#3F3F46', | |
zinc800: '#27272A', | |
zinc900: '#18181B', | |
neutral50: '#FAFAFA', | |
neutral100: '#F5F5F5', | |
neutral200: '#E5E5E5', | |
neutral300: '#D4D4D4', | |
neutral400: '#A3A3A3', | |
neutral500: '#737373', | |
neutral600: '#525252', | |
neutral700: '#404040', | |
neutral800: '#262626', | |
neutral900: '#171717', | |
stone50: '#FAFAF9', | |
stone100: '#F5F5F4', | |
stone200: '#E7E5E4', | |
stone300: '#D6D3D1', | |
stone400: '#A8A29E', | |
stone500: '#78716C', | |
stone600: '#57534E', | |
stone700: '#44403C', | |
stone800: '#292524', | |
stone900: '#1C1917', | |
red50: '#FEF2F2', | |
red100: '#FEE2E2', | |
red200: '#FECACA', | |
red300: '#FCA5A5', | |
red400: '#F87171', | |
red500: '#EF4444', | |
red600: '#DC2626', | |
red700: '#B91C1C', | |
red800: '#991B1B', | |
red900: '#7F1D1D', | |
orange50: '#FFF7ED', | |
orange100: '#FFEDD5', | |
orange200: '#FED7AA', | |
orange300: '#FDBA74', | |
orange400: '#FB923C', | |
orange500: '#F97316', | |
orange600: '#EA580C', | |
orange700: '#C2410C', | |
orange800: '#9A3412', | |
orange900: '#7C2D12', | |
amber50: '#FFFBEB', | |
amber100: '#FEF3C7', | |
amber200: '#FDE68A', | |
amber300: '#FCD34D', | |
amber400: '#FBBF24', | |
amber500: '#F59E0B', | |
amber600: '#D97706', | |
amber700: '#B45309', | |
amber800: '#92400E', | |
amber900: '#78350F', | |
yellow50: '#FEFCE8', | |
yellow100: '#FEF9C3', | |
yellow200: '#FEF08A', | |
yellow300: '#FDE047', | |
yellow400: '#FACC15', | |
yellow500: '#EAB308', | |
yellow600: '#CA8A04', | |
yellow700: '#A16207', | |
yellow800: '#854D0E', | |
yellow900: '#713F12', | |
lime50: '#F7FEE7', | |
lime100: '#ECFCCB', | |
lime200: '#D9F99D', | |
lime300: '#BEF264', | |
lime400: '#A3E635', | |
lime500: '#84CC16', | |
lime600: '#65A30D', | |
lime700: '#4D7C0F', | |
lime800: '#3F6212', | |
lime900: '#365314', | |
green50: '#F0FDF4', | |
green100: '#DCFCE7', | |
green200: '#BBF7D0', | |
green300: '#86EFAC', | |
green400: '#4ADE80', | |
green500: '#22C55E', | |
green600: '#16A34A', | |
green700: '#15803D', | |
green800: '#166534', | |
green900: '#14532D', | |
emerald50: '#ECFDF5', | |
emerald100: '#D1FAE5', | |
emerald200: '#A7F3D0', | |
emerald300: '#6EE7B7', | |
emerald400: '#34D399', | |
emerald500: '#10B981', | |
emerald600: '#059669', | |
emerald700: '#047857', | |
emerald800: '#065F46', | |
emerald900: '#064E3B', | |
teal50: '#F0FDFA', | |
teal100: '#CCFBF1', | |
teal200: '#99F6E4', | |
teal300: '#5EEAD4', | |
teal400: '#2DD4BF', | |
teal500: '#14B8A6', | |
teal600: '#0D9488', | |
teal700: '#0F766E', | |
teal800: '#115E59', | |
teal900: '#134E4A', | |
cyan50: '#ECFEFF', | |
cyan100: '#CFFAFE', | |
cyan200: '#A5F3FC', | |
cyan300: '#67E8F9', | |
cyan400: '#22D3EE', | |
cyan500: '#06B6D4', | |
cyan600: '#0891B2', | |
cyan700: '#0E7490', | |
cyan800: '#155E75', | |
cyan900: '#164E63', | |
sky50: '#F0F9FF', | |
sky100: '#E0F2FE', | |
sky200: '#BAE6FD', | |
sky300: '#7DD3FC', | |
sky400: '#38BDF8', | |
sky500: '#0EA5E9', | |
sky600: '#0284C7', | |
sky700: '#0369A1', | |
sky800: '#075985', | |
sky900: '#0C4A6E', | |
blue50: '#EFF6FF', | |
blue100: '#DBEAFE', | |
blue200: '#BFDBFE', | |
blue300: '#93C5FD', | |
blue400: '#60A5FA', | |
blue500: '#3B82F6', | |
blue600: '#2563EB', | |
blue700: '#1D4ED8', | |
blue800: '#1E40AF', | |
blue900: '#1E3A8A', | |
indigo50: '#EEF2FF', | |
indigo100: '#E0E7FF', | |
indigo200: '#C7D2FE', | |
indigo300: '#A5B4FC', | |
indigo400: '#818CF8', | |
indigo500: '#6366F1', | |
indigo600: '#4F46E5', | |
indigo700: '#4338CA', | |
indigo800: '#3730A3', | |
indigo900: '#312E81', | |
violet50: '#F5F3FF', | |
violet100: '#EDE9FE', | |
violet200: '#DDD6FE', | |
violet300: '#C4B5FD', | |
violet400: '#A78BFA', | |
violet500: '#8B5CF6', | |
violet600: '#7C3AED', | |
violet700: '#6D28D9', | |
violet800: '#5B21B6', | |
violet900: '#4C1D95', | |
purple50: '#FAF5FF', | |
purple100: '#F3E8FF', | |
purple200: '#E9D5FF', | |
purple300: '#D8B4FE', | |
purple400: '#C084FC', | |
purple500: '#A855F7', | |
purple600: '#9333EA', | |
purple700: '#7E22CE', | |
purple800: '#6B21A8', | |
purple900: '#581C87', | |
fuchsia50: '#FDF4FF', | |
fuchsia100: '#FAE8FF', | |
fuchsia200: '#F5D0FE', | |
fuchsia300: '#F0ABFC', | |
fuchsia400: '#E879F9', | |
fuchsia500: '#D946EF', | |
fuchsia600: '#C026D3', | |
fuchsia700: '#A21CAF', | |
fuchsia800: '#86198F', | |
fuchsia900: '#701A75', | |
pink50: '#FDF2F8', | |
pink100: '#FCE7F3', | |
pink200: '#FBCFE8', | |
pink300: '#F9A8D4', | |
pink400: '#F472B6', | |
pink500: '#EC4899', | |
pink600: '#DB2777', | |
pink700: '#BE185D', | |
pink800: '#9D174D', | |
pink900: '#831843', | |
rose50: '#FFF1F2', | |
rose100: '#FFE4E6', | |
rose200: '#FECDD3', | |
rose300: '#FDA4AF', | |
rose400: '#FB7185', | |
rose500: '#F43F5E', | |
rose600: '#E11D48', | |
rose700: '#BE123C', | |
rose800: '#9F1239', | |
rose900: '#881337', | |
}, | |
fonts: { | |
sans: '"IBM Plex Sans", system-ui, sans-serif', | |
}, | |
fontWeights: { | |
normal: 400, | |
bold: 600, | |
}, | |
fontSizes: { | |
12: '0.75rem', | |
14: '0.875rem', | |
16: '1rem', | |
18: '1.125rem', | |
20: '1.25rem', | |
24: '1.5rem', | |
28: '1.75rem', | |
}, | |
transitions: { | |
easeInOut: '0.2s ease-in-out', | |
}, | |
space: { | |
0: '0', | |
4: '0.25rem', | |
8: '0.5rem', | |
12: '0.75rem', | |
14: '0.875rem', | |
16: '1rem', | |
18: '1.25rem', | |
}, | |
radii: { | |
4: '0.25rem', | |
8: '0.5rem', | |
}, | |
}, | |
}) | |
const globalStyles = globalCss({ | |
'*, *::before, *::after': { | |
boxSizing: 'border-box', | |
}, | |
'*': { | |
margin: 0, | |
}, | |
html: { | |
fontSize: '100%', | |
}, | |
body: { | |
lineHeight: 1.5, | |
'-webkit-font-smoothing': 'antialiased', | |
fontFamily: '$sans', | |
}, | |
'img, picture, video, canvas, svg': { | |
display: 'block', | |
maxWidth: '100%', | |
}, | |
'input, button, textarea, select': { | |
font: 'inherit', | |
}, | |
'p, h1, h2, h3, h4, h5, h6': { | |
overflowWrap: 'break-word', | |
}, | |
'#root, #__next': { | |
isolation: 'isolate', | |
}, | |
'#root': { | |
height: '100vh', | |
}, | |
}) | |
export { | |
styled, | |
css, | |
globalCss, | |
keyframes, | |
getCssText, | |
theme, | |
createTheme, | |
config, | |
globalStyles, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment