1 | ---
|
2 | title: Box
|
3 | storybookPath: page-layout-box--default
|
4 | isExperimentalPackage: false
|
5 | ---
|
6 |
|
7 | The Box component is our lowest-level primitive. The goal of the Box component
|
8 | is to map props to our tokens so that consumers should be able to use these
|
9 | props for all of their styling needs. Ideally, you shouldn't need to use the
|
10 | `className` or `style` prop at all. Internally, all Spark Web components are
|
11 | composed using the Box component.
|
12 |
|
13 | Box is a polymorphic component (meaning it will render different elements
|
14 | depending on what is provided to the `as` prop). If no `as` prop is provided,
|
15 | Box will render a `div`.
|
16 |
|
17 | We also spread in consumer props, so any valid HTML attributes are also valid
|
18 | for Box. Due to some clever TypeScript we can even warn you when you use an
|
19 | invalid combination of props.
|
20 |
|
21 | ```jsx
|
22 | <Box as="input" href="https://spark.brighte.com.au" />
|
23 | ```
|
24 |
|
25 | In the example above, you should see a "red squiggly" under the `href` element
|
26 | with the following error:
|
27 |
|
28 | ```console
|
29 | Type '{ as: "input"; href: string; }' is not assignable to type 'IntrinsicAttributes & { as?: "input" | undefined; ref?: Ref<HTMLInputElement> | undefined; } & Omit<Pick<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "key" | keyof InputHTMLAttributes<...>>, "as"> & { ...; } & UnresponsiveBoxProps & ResponsiveBoxProps'.
|
30 | Property 'href' does not exist on type 'IntrinsicAttributes & { as?: "input" | undefined; ref?: Ref<HTMLInputElement> | undefined; } & Omit<Pick<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "key" | keyof InputHTMLAttributes<...>>, "as"> & { ...; } & UnresponsiveBoxProps & ResponsiveBoxProps'. Did you mean 'ref'?
|
31 | ```
|
32 |
|
33 | Unfortunately TypeScript errors can be pretty cryptic sometimes 🙃.
|
34 |
|
35 | **Pro tip**: copying the error message and pasting it into
|
36 | [TypeScript Error Translator](https://ts-error-translator.vercel.app/) can
|
37 | sometimes help make these errors easier to understand.
|
38 |
|
39 | A minimal CSS reset is also applied to each element rendered with Box. This
|
40 | helps prevent styles from "leaking" out and effecting other elements (which
|
41 | should make it easier to incrementally adopt Spark Web into existing projects).
|
42 | If you provide a non-Spark component to the `as` prop instead of an element, it
|
43 | is recommended that you also use the `asElement` prop so the appropriate styles
|
44 | can be applied.
|
45 |
|
46 | ## Examples
|
47 |
|
48 | ### Responsive styles
|
49 |
|
50 | Most of Box's props accept a string value (usually corresponding to a token), we
|
51 | also accept an object for responsive styles.
|
52 |
|
53 | ```jsx live
|
54 | const items = [1, 2, 3];
|
55 |
|
56 | return (
|
57 | <Box
|
58 | background="primaryLow"
|
59 | display="inline-flex"
|
60 | flexDirection={{ mobile: 'row', tablet: 'column', desktop: 'rowReverse' }}
|
61 | gap={{ mobile: 'small', tablet: 'medium', desktop: 'large' }}
|
62 | padding={{ mobile: 'small', tablet: 'medium', desktop: 'large' }}
|
63 | >
|
64 | {items.map(item => (
|
65 | <Row
|
66 | key={item}
|
67 | align="center"
|
68 | alignY="center"
|
69 | background="primary"
|
70 | height={{ mobile: 'small', tablet: 'medium', desktop: 'large' }}
|
71 | width={{ mobile: 'small', tablet: 'medium', desktop: 'large' }}
|
72 | >
|
73 | <Text>{item}</Text>
|
74 | </Row>
|
75 | ))}
|
76 | </Box>
|
77 | );
|
78 | ```
|
79 |
|
80 | Resize your browser to see the example above change at different breakpoints.
|
81 |
|
82 | ### Backgrounds
|
83 |
|
84 | Box stores the background in a provider. We can use this to work out what colour
|
85 | to use by default for text elements.
|
86 |
|
87 | ```jsx live
|
88 | const backgroundTones = [
|
89 | // Light
|
90 | ['surface', 'positiveLight', 'infoLight', 'cautionLight', 'criticalLight'],
|
91 | // Dark
|
92 | ['muted', 'positive', 'info', 'caution', 'critical'],
|
93 | ];
|
94 |
|
95 | return (
|
96 | <Stack gap="large">
|
97 | {backgroundTones.map((backgrounds, index) => (
|
98 | <Columns
|
99 | key={index}
|
100 | collapseBelow="tablet"
|
101 | gap="large"
|
102 | template={[1, 1, 1]}
|
103 | >
|
104 | {backgrounds.map(background => (
|
105 | <Box
|
106 | key={background}
|
107 | shadow="medium"
|
108 | background={background}
|
109 | height="large"
|
110 | display="flex"
|
111 | flexShrink={0}
|
112 | alignItems="center"
|
113 | justifyContent="center"
|
114 | >
|
115 | <Text weight="semibold">{background}</Text>
|
116 | </Box>
|
117 | ))}
|
118 | </Columns>
|
119 | ))}
|
120 | </Stack>
|
121 | );
|
122 | ```
|
123 |
|
124 | Notice that the Text in the example above doesn't use the tone prop, the colour
|
125 | is worked out using the BackgroundProvider in Box.
|
126 |
|
127 | **Note:** this will only work if you use the `background` prop. If you try to
|
128 | style the background colour in any other way the Text component will not know
|
129 | what colour the background is and so cannot invert its colour to make sure that
|
130 | its contents are readable.
|
131 |
|
132 | ```jsx
|
133 | <Box style={{ background: 'midnightblue' }} padding="large">
|
134 | <Text>Good luck reading this!</Text>
|
135 | </Box>
|
136 | ```
|
137 |
|
138 | ## Props
|
139 |
|
140 | <PropsTable displayName="Box" />
|
141 |
|
142 | By default, Box renders a `div` element. You can customise this via the `as`
|
143 | prop. Extra props will also be forwarded to the underlying element.
|