⚡ A simple set of UI development helpers for React projects
Using Yarn:
$ yarn add @gsandf/ui react react-dom styled-components@^5.3.0
…or using [npm]:
$ npm i --save @gsandf/ui react react-dom styled-components@^5.3.0
If you plan to use the components, most require a ThemeProvider
be set up.
You can do this easily using the default theme. For example:
import { createTheme, defaultTheme } from '@gsandf/ui';
import { ThemeProvider } from 'styled-components';
export const theme = createTheme({ ...defaultTheme });
const GlobalStyles = theme.styles;
export default function App({ children }) {
return (
<ThemeProvider theme={theme}>
<GlobalStyles />
{children}
</ThemeProvider>
);
}
// If you use TypeScript, add an interface for the theme:
export type Theme = typeof theme;
declare module 'styled-components' {
interface DefaultTheme extends Theme {}
}
To see how to customize the theme, check out the Theme section.
<Button />
- A simple button component with basic default styles
<AspectRatio />
- A<Box />
with a fixed width/height ratio<BasicGrid />
- A basic grid component that distributes its children evenly<Box />
- A simple box. By default, it renders a<div />
.<Center />
- A<Flex />
with flex properties set to center content.<Container />
- Constrains content width with a default max size<Flex />
- A box withdisplay: flex
<Hide />
- Helper to setdisplay: none
based on a media query<NoServerRender />
- Renders its children only on the client after hydration.<ScreenReaderContent />
- Hides content visually but remains accessible to screen readers.<Stack />
-Stack
is aFlex
with helpers to add spacing between elements. The default direction is a column.<HStack />
-HStack
is aFlex
with helpers to add spacing between elements. It lays out its children horizontally and centers them vertically.<VStack />
-VStack
is aFlex
with helpers to add spacing between elements. It lays out its children vertically and centers them horizontally.
<Text />
- Render text. It accepts all theme mixins as props, has helpers for truncating text, and by default retains newlines (for rendering text from an API response).
Returns the name of the current breakpoint. If breakpointFilter
is given, returns the name of the largest breakpoint that matches the current window.
function BasicUsage() {
const breakpoint = useBreakpoint();
return <div>The current breakpoint is {breakpoint}</div>;
}
function WithFilteredBreakpoints() {
const breakpoint = useBreakpoint(['md', 'xxl']);
return (
<div>
Of `base`, `md`, and `xxl`, the current breakpoint is {breakpoint}
</div>
);
}
Returns a value based on the current window size.
function BasicUsage() {
const bgColor = useBreakpointValue({
base: 'gray900',
sm: 'gray700',
md: 'gray600',
lg: 'gray400',
xl: 'gray200',
xxl: 'gray100'
});
const fgColor = useBreakpointValue({
base: 'black',
lg: 'white'
});
return (
<Center $bgColor={bgColor} $color={fgColor}>
Adjust the screen size!
</Center>
);
}
Handles click events outside a DOM element, like a div
. A handler function is invoked when a click or touch event happens outside the referenced element.
function Example() {
const containerRef = useRef(null);
useClickedOutside(containerRef, () => alert('clicked outside'));
return (
<Center>
<VStack>
<Box ref={containerRef}>
Click outside this box to trigger an alert!
</Box>
</VStack>
</Center>
);
}
Saves a value like useState
, but delays setting the value until delay
milliseconds has passed.
function Example() {
const [triggerCount, setTriggerCount] = useDebouncedState(0);
const incrementTriggerCount = () => {
setTriggerCount(n => n + 1);
};
useEffect(() => {
document.addEventListener('mousemove', incrementTriggerCount);
return () => {
document.removeEventListener('mousemove', incrementTriggerCount);
};
});
return <div>Times triggered: {triggerCount}</div>;
}
Saves a value in localStorage
so it can be persisted between page refresh. This hook is used similar to useState
, but the first argument is the key used to save/lookup the value in localStorage
.
If localStorage
isn't available - such as during a server render - the initial value will be returned.
function Example() {
const [value, setValue] = useLocalStorage('exampleKey', 0);
return (
<div>
<Box>Value stored in localStorage: {value}</Box>
<HStack>
<Button onClick={() => setValue(value => value - 1)}>-</Button>
<Button onClick={() => setValue(0)}>Reset</Button>
<Button onClick={() => setValue(value => value + 1)}>+</Button>
</HStack>
</div>
);
}
Detects when a media query matches the current window.
function Example() {
const isLargerThan1000 = useMediaQuery('(min-width: 1000px)');
return (
<Box>
Is the screen at least 1000px wide? {isLargerThan1000 ? 'yes' : 'no'}
</Box>
);
}
Calls the given onClose
function when a common event outside a modal should trigger a close. For example, this can handle when the Escape key is pressed and when the modal is clicked outside.
function ExampleModal({ onClose }) {
const containerRef = useRef(null);
useModalCloseEvent(onClose, containerRef);
return (
<VStack ref={containerRef}>
<Box>
This modal will close if the escape key is pressed or if clicked outside
of.
</Box>
</VStack>
);
}
A theme is an object that follows a certain shape. The idea for the shape was originally taken from the System UI theme specification. Most of the theme shouldn't rely on a certain build system, framework, etc.
For the most part, we've kept the same ideas, but we allow relying on certain styling ideas (e.g. media queries, global styles, component styles) that may rely on certain tools being available.
Here's the interface for a theme:
interface CustomTheme {
borders?: Record<string, CSSProperties['border']>;
breakpoints?: Record<string, number>;
colors?: Record<string, CSSProperties['backgroundColor']>;
components?: ComponentStyles;
fonts?: Record<string, CSSProperties['fontFamily']>;
fontSizes?: CSSProperties['fontSize'][];
fontWeights?: Record<string, CSSProperties['fontWeight']>;
lineHeights?: Record<string, CSSProperties['lineHeight']>;
radii?: Record<string, CSSProperties['borderRadius']>;
shadows?: Record<string, CSSProperties['boxShadow']>;
sizes?: Record<string | number, string>;
space?: (string | number)[] | Record<string | number, string>;
styles?: GlobalStyleComponent<unknown, unknown> | (() => ReactElement);
transitions?: GlobalStyleComponent<unknown, CSSProperties['transition']>;
zIndices?: Record<string, CSSProperties['zIndex']>;
}
export type ComponentStyles = Record<
string,
{
baseStyle?: CSSProp<unknown>;
variants?: Record<string, CSSProp<unknown>>;
}
>;
A base reference theme that can be expanded easily.
import { defaultTheme } from '@gsandf/ui';
Takes a theme object and extends it with media
and mixins
to it. media
is a set of CSS breakpoint helpers. mixins
are a set of utilities for making flexible styled components.
import { defaultTheme } from '@gsandf/ui';
const coolWebsiteTheme = {
...defaultTheme,
colors: {
primary: '#f00',
onPrimary: '#fff',
secondary: '#ff0',
onSecondary: '#000',
transparent: 'transparent',
white: '#ffffff'
},
fonts: {
body: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
heading:
'Roboto Condensed, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
monospace: 'IBM Plex Mono, Menlo, monospace'
},
lineHeights: {
body: 1.7,
control: 1.2,
heading: 1.2
},
shadows: {
default: `0 0 2px 0 #00000004`
}
};
const theme = createTheme(coolWebsiteTheme);
isServer
/isBrowser
- booleans for checking if the current rendering environment is on the server or in a browser.ensureUnit(value, unit)
- Makes sure a given value has a unit at the end. The default unit ispx
. Also accepts an array of values.noop()
- Empty functionomit(object, key)
- Shallow omit a property from an objecttype StyledComponentProps<T>
- Helper to extract type of props from a Styled Component.
Node.js and Yarn are required to work with this project.
To install all dependencies, run:
yarn
Then, you can start Storybook to test changes:
yarn dev
See below for other scripts.
yarn build |
Builds the project to ./dist |
yarn dev |
Starts the Storybook server |
yarn test |
Run project tests |
yarn test --watch |
Run project tests, watching for file changes |
yarn type-check |
Check the project for TypeScript errors |
yarn validate |
Runs tests, checks formatting, lints code, and checks for TypeScript errors |
UNLICENSED