Hone UI

The sx prop

Style any component with sx. Static values compile to CSS, and theme functions run in the browser.

sx takes a style object, an array of them, or a function of the theme. The @hone-ui/sx build plugin compiles static objects to CSS, so nothing styles at runtime.

<Box sx={{ p: 2, color: 'primary.main', '&:hover': { opacity: 0.8 } }} />

Custom breakpoints

Media queries can't read CSS variables, so the plugin needs its own copy of your breakpoints. Put them in one object and give it to both createTheme and the plugin. The object has the same shape as the theme option: values and an optional unit.

breakpoints.ts
export const breakpoints = {
  values: { mobile: 0, tablet: 640, laptop: 1024, desktop: 1200 },
};
vite.config.ts
import sx from '@hone-ui/sx/vite';
import { breakpoints } from './src/breakpoints';

export default { plugins: [sx({ breakpoints })] };
next.config.ts
import { withSx } from '@hone-ui/sx/next';
import { breakpoints } from './src/breakpoints';

export default withSx({}, { breakpoints });
theme.ts
import { createTheme } from '@hone-ui/react';
import { breakpoints } from './breakpoints';

export const theme = createTheme({ breakpoints });

Responsive objects and arrays then use your names, smallest first. { mobile: 1, tablet: 2 } compiles to a base rule and a min-width:640px rule. [1, null, 3] maps to mobile, tablet and laptop in order. A written @media (min-width:640px) key, such as the string theme.breakpoints.up('tablet') returns, joins the tablet rule.

Custom names replace the defaults, so xs to xl stop working. Overriding only default names ({ values: { md: 1000 } }) keeps the others. For TypeScript, add your names to BreakpointOverrides. The fallback for uncompiled objects reads the same names from the theme, but keeps only the smallest breakpoint.

Stack keeps the default breakpoints.

Custom palette colors

sx resolves 'neutral.main' only if the plugin knows the palette key neutral. Pass the same palette object to createTheme and to the plugin, as with breakpoints. The theming guide has the setup.

Functions of the theme

A function receives the current theme. Arrays can hold functions, and a single property can be a function.

import { Backdrop, Box } from '@hone-ui/react';

<Backdrop open sx={(theme) => ({ color: '#fff', zIndex: theme.zIndex.drawer + 1 })} />;
<Box sx={[{ p: 2 }, (theme) => ({ bgcolor: theme.vars.palette.background.paper })]} />;
<Box sx={{ color: (theme) => theme.vars.palette.primary.main }} />;

Read colors from theme.vars.palette. Its values are CSS variables, so they follow dark mode; theme.palette holds one scheme's values and stays light in dark mode.

The plugin leaves these to the component, which calls the function with the theme from the nearest ThemeProvider and applies the result as inline styles. In an array, entries before the first function compile. Entries after it stay inline too, so they still override the function. Put a function last to keep the most entries compiled.

Because the result is inline styles, a function cannot return selectors ('&:hover'), media queries or breakpoints above the smallest one (xs unless your theme names them differently). A development warning names what was dropped. Put those in a static object next to the function.

Client components only

A function cannot cross from a Server Component to a Client Component, so write function sx inside a file marked 'use client'. The plugin does not know whether a file is a Server Component, so it does not check. A server-rendered component such as Box throws an error that says to add 'use client', and Next.js reports the file when a Server Component passes a function to a Client Component.

On this page