hope-ui-solid (WIP)
![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)
🤞 The hoped component library for your SolidJS applications.
Getting started
Installation
First install Hope UI and Stitches as a dependency.
npm :
npm install hope-ui-solid @stitches/core
Yarn :
yarn add hope-ui-solid @stitches/core
pnpm :
pnpm add hope-ui-solid @stitches/core
Provider setup
After installing Hope UI, you need to set up the HopeProvider
at the root of your application.
This can be either in your index.jsx
, index.tsx
or your App component.
import { HopeProvider } from "hope-ui-solid";
function App() {
return (
<HopeProvider>
<MyApp />
</HopeProvider>
);
}
Component documentation
All component's documentation can be found here and you can try them in the Storybook.
Creating styles
To create custom styles use the css()
method provided by Stitches.
It's the core Stitches API with no extra layer, you can find the documentation here.
For convenience Hope UI re-export it configured with its default theme.
import { css } from "hope-ui-solid";
const buttonStyles = css({
backgroundColor: "red",
borderRadius: "16px",
fontSize: "16px",
padding: "8px 16px",
"&:hover": {
backgroundColor: "orange",
},
});
function Button() {
return <button className={buttonStyles()}>Button</button>;
}
Using Hope UI theme tokens
Many css propeties of Stitches css
method and css
prop are mapped to scale from the Hope UI theme tokens, meaning you can set they value based on the Hope UI theme.
You can view the props/token mapping on the Stitches docs
Usage
To apply a token you need to prefix it with a $
sign.
import { css } from "hope-ui-solid";
const buttonStyles = css({
backgroundColor: "$primary500",
borderRadius: "$full",
fontSize: "$base",
padding: "$4 $8",
"&:hover": {
backgroundColor: "$primary600",
},
});
function Button() {
return <button className={buttonStyles()}>Button</button>;
}
Available tokens
Colors
export default {
colors: {
white: "#ffffff",
black: "#000000",
transparent: "transparent",
primary50: "#eff6ff",
primary100: "#dbeafe",
primary200: "#bfdbfe",
primary300: "#93c5fd",
primary400: "#60a5fa",
primary500: "#3b82f6",
primary600: "#2563eb",
primary700: "#1d4ed8",
primary800: "#1e40af",
primary900: "#1e3a8a",
dark50: "#c1c2c5",
dark100: "#a6a7ab",
dark200: "#909296",
dark300: "#5c5f66",
dark400: "#373a40",
dark500: "#2c2e33",
dark600: "#25262b",
dark700: "#1a1b1e",
dark800: "#141517",
dark900: "#101113",
neutral50: "#f9fafb",
neutral100: "#f3f4f6",
neutral200: "#e5e7eb",
neutral300: "#d1d5db",
neutral400: "#9ca3af",
neutral500: "#6b7280",
neutral600: "#4b5563",
neutral700: "#374151",
neutral800: "#1f2937",
neutral900: "#111827",
success50: "#ecfdf5",
success100: "#d1fae5",
success200: "#a7f3d0",
success300: "#6ee7b7",
success400: "#34d399",
success500: "#10b981",
success600: "#059669",
success700: "#047857",
success800: "#065f46",
success900: "#064e3b",
info50: "#f0f9ff",
info100: "#e0f2fe",
info200: "#bae6fd",
info300: "#7dd3fc",
info400: "#38bdf8",
info500: "#0ea5e9",
info600: "#0284c7",
info700: "#0369a1",
info800: "#075985",
info900: "#0c4a6e",
warning50: "#fffbeb",
warning100: "#fef3c7",
warning200: "#fde68a",
warning300: "#fcd34d",
warning400: "#fbbf24",
warning500: "#f59e0b",
warning600: "#d97706",
warning700: "#b45309",
warning800: "#92400e",
warning900: "#78350f",
danger50: "#fef2f2",
danger100: "#fee2e2",
danger200: "#fecaca",
danger300: "#fca5a5",
danger400: "#f87171",
danger500: "#ef4444",
danger600: "#dc2626",
danger700: "#b91c1c",
danger800: "#991b1b",
danger900: "#7f1d1d",
},
};
Typography
export default {
fonts: {
sans: 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol","Noto Color Emoji"',
serif: 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif',
mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
},
fontSizes: {
xs: "0.75rem",
sm: "0.875rem",
base: "1rem",
lg: "1.125rem",
xl: "1.25rem",
"2xl": "1.5rem",
"3xl": "1.875rem",
"4xl": "2.25rem",
"5xl": "3rem",
"6xl": "3.75rem",
"7xl": "4.5rem",
"8xl": "6rem",
"9xl": "8rem",
},
fontWeights: {
hairline: 100,
thin: 200,
light: 300,
normal: 400,
medium: 500,
semibold: 600,
bold: 700,
extrabold: 800,
black: 900,
},
lineHeights: {
normal: "normal",
none: 1,
shorter: 1.25,
short: 1.375,
base: 1.5,
tall: 1.625,
taller: 2,
3: ".75rem",
4: "1rem",
5: "1.25rem",
6: "1.5rem",
7: "1.75rem",
8: "2rem",
9: "2.25rem",
10: "2.5rem",
},
letterSpacings: {
tighter: "-0.05em",
tight: "-0.025em",
normal: "0",
wide: "0.025em",
wider: "0.05em",
widest: "0.1em",
},
};
Space
export default {
space: {
0: "0",
px: "1px",
0_5: "0.125rem",
1: "0.25rem",
1_5: "0.375rem",
2: "0.5rem",
2_5: "0.625rem",
3: "0.75rem",
3_5: "0.875rem",
4: "1rem",
5: "1.25rem",
6: "1.5rem",
7: "1.75rem",
8: "2rem",
9: "2.25rem",
10: "2.5rem",
12: "3rem",
14: "3.5rem",
16: "4rem",
20: "5rem",
24: "6rem",
28: "7rem",
32: "8rem",
36: "9rem",
40: "10rem",
44: "11rem",
48: "12rem",
52: "13rem",
56: "14rem",
60: "15rem",
64: "16rem",
72: "18rem",
80: "20rem",
96: "24rem",
},
};
Sizes
export default {
sizes: {
...space,
max: "max-content",
min: "min-content",
full: "100%",
xs: "20rem",
sm: "24rem",
md: "28rem",
lg: "32rem",
xl: "36rem",
"2xl": "42rem",
"3xl": "48rem",
"4xl": "56rem",
"5xl": "64rem",
"6xl": "72rem",
"7xl": "80rem",
"8xl": "90rem",
containerSm: "640px",
containerMd: "768px",
containerLg: "1024px",
containerXl: "1280px",
container2xl: "1536px",
},
};
Border radius
export default {
radii: {
none: "0",
xs: "0.125rem",
sm: "0.25rem",
md: "0.375rem",
lg: "0.5rem",
xl: "0.75rem",
"2xl": "1rem",
"3xl": "1.5rem",
full: "9999px",
},
};
Shadows
export default {
shadows: {
none: "0 0 #0000",
xs: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
sm: "0 1px 3px 0 rgb(0 0 0 / 0.09), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
md: "0 4px 6px -1px rgb(0 0 0 / 0.09), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
lg: "0 10px 15px -3px rgb(0 0 0 / 0.09), 0 4px 6px -4px rgb(0 0 0 / 0.1)",
xl: "0 20px 25px -5px rgb(0 0 0 / 0.09), 0 8px 10px -6px rgb(0 0 0 / 0.1)",
"2xl": "0 25px 50px -12px rgb(0 0 0 / 0.24)",
inner: "inset 0 3px 6px 0 rgb(0 0 0 / 0.4)",
},
};
Z-index values
export default {
zIndices: {
hide: -1,
auto: "auto",
base: 0,
docked: 10,
dropdown: 1000,
sticky: 1100,
banner: 1200,
overlay: 1300,
modal: 1400,
popover: 1500,
skipLink: 1600,
toast: 1700,
tooltip: 1800,
},
};
Customizing Theme
If you intend to customise the default theme
object to match your design requirements, you can extend the theme from hope-ui-solid
.
Hope UI provides an extendTheme
function that deep merges the default theme with your customizations.
import { extendTheme, HopeProvider } from "hope-ui-solid";
const theme = extendTheme({
tokens: {
colors: {
primary500: "salmon",
},
fonts: {
sans: "Inter, sans-serif",
},
},
components: {
Button: {
defaultProps: {
variant: "outline",
radius: "full",
uppercase: true,
},
},
},
});
function App() {
return (
<HopeProvider theme={theme}>
<MyApp />
</HopeProvider>
);
}
The style props
All Hope UI components provides some utility style props that are mapped to a theme scale.
Style props are a way to alter the style of a component by simply passing props to it. It helps to save time by providing helpful shorthand ways to style components.
⚠️ You can't put arbitrary values in style props.
Usage
<Center color="white" bg="info500" w="40" h="24">
This is the Center.
</Center>
Available props
The following table shows a list of every style prop and the properties within each group.
Color
Prop | CSS property | Theme token |
---|
color | color | colors |
bg | background | colors |
Margin
Prop | CSS property | Theme token |
---|
m | margin | space |
mx | margin-inline-start + margin-inline-end | space |
my | margin-top + margin-bottom | space |
mt | margin-top | space |
mr | margin-right | space |
mb | margin-bottom | space |
ml | margin-left | space |
Padding
Prop | CSS property | Theme token |
---|
p | padding | space |
px | padding-inline-start + padding-inline-end | space |
py | padding-top + padding-bottom | space |
pt | padding-top | space |
pr | padding-right | space |
pb | padding-bottom | space |
pl | padding-left | space |
Typography
Prop | CSS property | Theme token |
---|
fontFamily | font-family | fonts |
fontSize | font-size | fontSizes |
fontWeight | font-weight | fontWeights |
lineHeight | line-height | lineHeights |
letterSpacing | letter-spacing | letterSpacings |
textAlign | text-align | none |
fontStyle | font-style | none |
textTransform | text-transform | none |
lineClamp | custom utility to truncate text to a given number of lines | none |
Layout
Prop | CSS property | Theme token |
---|
display | display | none |
verticalAlign | vertical-align | none |
overflow | overflow | none |
overflowX | overflow-x | none |
overflowY | overflow-y | none |
Size
Prop | CSS property | Theme token |
---|
w | width | sizes |
minW | min-width | sizes |
maxW | max-width | sizes |
h | height | sizes |
minH | min-height | sizes |
maxH | max-height | sizes |
boxSize | width + height | sizes |
Flexbox
Prop | CSS property | Theme token |
---|
alignItems | align-items | none |
alignContent | align-content | none |
justifyItems | justify-items | none |
justifyContent | justify-content | none |
flexWrap, wrap* | flex-wrap | none |
flexDirection, direction* | flex-direction | none |
flex | flex | none |
grow | flex-grow | none |
shrink | flex-shrink | none |
justifySelf | justify-self | none |
alignSelf | align-self | none |
order | order | none |
Note: Props in * will only work if you use the Flex component.
Grid
Prop | CSS property | Theme token |
---|
gap | gap | space |
rowGap | row-gap | space |
columnGap | column-gap | space |
gridColumnSpan | grid-column | none |
gridColumnStart | grid-column-start | none |
gridColumnEnd | grid-column-end | none |
gridRowSpan | grid-row | none |
gridRowStart | grid-row-start | none |
gridRowEnd | grid-row-end | none |
gridAutoFlow | grid-auto-flow | none |
gridAutoColumns | grid-auto-columns | none |
gridAutoRows | grid-auto-rows | none |
gridTemplateColumns | grid-template-column | none |
gridTemplateRows | grid-template-rows | none |
placeItems | place-items | none |
placeContent | place-content | none |
placeSelf | place-self | none |
Border
Prop | CSS property | Theme token |
---|
borderWidth | border-width | none |
borderStyle | border-style | none |
borderColor | border-color | colors |
borderRadius | border-radius | radii |
Position
Prop | CSS property | Theme token |
---|
position | position | none |
zIndex | z-index | zIndices |
top | top | space |
right | right | space |
left | left | space |
bottom | bottom | space |
Shadow
Prop | CSS property | Theme token |
---|
boxShadow | box-shadow | shadows |
The css
prop
All Hope UI components provides a css
prop for overriding styles easily.
It’s like the style attribute, but it supports tokens, media queries, nesting and token-aware values.
<Button
css={{
bg: "$primary500",
borderRadius: "$none",
color: "white",
"&:hover": {
bg: "$primary600",
},
}}
>
Button
</Button>
Targeting other Hope UI components
Inside the css
props you can target other Hope UI components using string interpolation in the selector.
<Center>
<Button
css={{
[`${Center} &`]: {
background: "red",
},
}}
>
Button
</Button>
</Center>
Targeting other SolidJS components
Inside the css
props you can target other SolidJS components using string interpolation in the selector.
function Button() {
return <button className="my-button">Button</button>;
}
Button.toString = () => ".my-button";
<Center
css={{
[`& ${Button}`]: {
background: "red",
},
}}
>
<Button />
</Center>;
The as
prop
All Hope UI components are polymorphic, meaning you can use the as
prop to change the rendered element.
If you are using TypeScript you will get proper auto-completion based on the value of the as
prop.
<Button as="a" href="https://www.solidjs.com/">
I'm an anchor tag
</Button>
And it works with SolidJS components too.
import { Router, Link } from "solid-app-router";
<Router>
<Button as={Link} href="/">
I'm a solid-app-router link
</Button>
</Router>;
Responsive styles
Hope UI provide the follow media queries.
Prop | CSS property |
---|
sm | @media (min-width: 640px) |
md | @media (min-width: 768px) |
lg | @media (min-width: 1024px) |
xl | @media (min-width: 1280px) |
2xl | @media (min-width: 1536px) |
reduce-motion | @media (prefers-reduced-motion: reduce) |
light | @media (prefers-color-scheme: light) |
dark | @media (prefers-color-scheme: dark) |
Usage with Stitches css
method
Variant created with Stitches css
method support responsive styles.
Each breakpoint is a key on the object prefixed by the @
symbol.
You must use the @initial
breakpoint to declare the initial variant before any breakpoints are applied.
For more in dept explanation please refer to the Stitches docs.
import { css } from "hope-ui-solid";
const cardStyles = css({
borderRadius: "$lg",
backgroundColor: "white",
variants: {
elevation: {
sm: {
boxShadow: "$sm",
},
md: {
boxShadow: "$md",
},
},
},
});
cardStyles({
elevation: {
"@initial": "sm",
"@lg": "md",
},
});
Usage with the css
prop
When using responsive styles with the css
prop each breakpoint is a key on the css object prefixed by the @
symbol.
Here you don't need the @initial
breakpoint.
For more in dept explanation please refer to the Stitches docs.
<Button
css={{
backgroundColor: "$primary500",
"@sm": {
backgroundColor: "$success500",
},
"@xl": {
backgroundColor: "$info500",
},
}}
>
Box
</Button>
SSR
Stitches have a section about SSR on their docs
For convenience Hope UI re-export the getCssText()
method from Stitches configured with its default theme.
import { getCssText } from "hope-ui-solid";
Acknowledgment
This project would not have been possible without these open source projects :
Contributors ✨
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!
License
This project is licensed under the MIT License.