Skip to content

Commit 5501fbb

Browse files
committed
Add css package
1 parent d88a198 commit 5501fbb

File tree

4 files changed

+655
-0
lines changed

4 files changed

+655
-0
lines changed

packages/css/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
# @theme-ui/css
3+
4+
Theme UI CSS lets you write style objects with responsive, theme-aware ergonomic shortcuts.
5+
This package powers the `sx` prop in Theme UI.
6+
7+
```sh
8+
npm i @theme-ui/css
9+
```

packages/css/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "@theme-ui/css",
3+
"version": "0.2.48",
4+
"main": "dist/index.js",
5+
"module": "dist/index.esm.js",
6+
"scripts": {
7+
"prepare": "microbundle --no-compress",
8+
"watch": "microbundle watch --no-compress"
9+
},
10+
"author": "Brent Jackson",
11+
"license": "MIT",
12+
"publishConfig": {
13+
"access": "public"
14+
}
15+
}

packages/css/src/index.js

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
export const get = (obj, key, def, p, undef) => {
2+
key = key && key.split ? key.split('.') : [key]
3+
for (p = 0; p < key.length; p++) {
4+
obj = obj ? obj[key[p]] : undef
5+
}
6+
return obj === undef ? def : obj
7+
}
8+
9+
const defaultBreakpoints = [40, 52, 64].map(n => n + 'em')
10+
11+
export const defaultTheme = {
12+
space: [0, 4, 8, 16, 32, 64, 128, 256, 512],
13+
fontSizes: [12, 14, 16, 20, 24, 32, 48, 64, 72],
14+
}
15+
16+
const aliases = {
17+
bg: 'backgroundColor',
18+
m: 'margin',
19+
mt: 'marginTop',
20+
mr: 'marginRight',
21+
mb: 'marginBottom',
22+
ml: 'marginLeft',
23+
mx: 'marginX',
24+
my: 'marginY',
25+
p: 'padding',
26+
pt: 'paddingTop',
27+
pr: 'paddingRight',
28+
pb: 'paddingBottom',
29+
pl: 'paddingLeft',
30+
px: 'paddingX',
31+
py: 'paddingY',
32+
}
33+
34+
const multiples = {
35+
marginX: ['marginLeft', 'marginRight'],
36+
marginY: ['marginTop', 'marginBottom'],
37+
paddingX: ['paddingLeft', 'paddingRight'],
38+
paddingY: ['paddingTop', 'paddingBottom'],
39+
size: ['width', 'height'],
40+
}
41+
42+
const scales = {
43+
color: 'colors',
44+
backgroundColor: 'colors',
45+
borderColor: 'colors',
46+
margin: 'space',
47+
marginTop: 'space',
48+
marginRight: 'space',
49+
marginBottom: 'space',
50+
marginLeft: 'space',
51+
marginX: 'space',
52+
marginY: 'space',
53+
padding: 'space',
54+
paddingTop: 'space',
55+
paddingRight: 'space',
56+
paddingBottom: 'space',
57+
paddingLeft: 'space',
58+
paddingX: 'space',
59+
paddingY: 'space',
60+
top: 'space',
61+
right: 'space',
62+
bottom: 'space',
63+
left: 'space',
64+
gridGap: 'space',
65+
gridColumnGap: 'space',
66+
gridRowGap: 'space',
67+
gap: 'space',
68+
columnGap: 'space',
69+
rowGap: 'space',
70+
fontFamily: 'fonts',
71+
fontSize: 'fontSizes',
72+
fontWeight: 'fontWeights',
73+
lineHeight: 'lineHeights',
74+
letterSpacing: 'letterSpacings',
75+
border: 'borders',
76+
borderTop: 'borders',
77+
borderRight: 'borders',
78+
borderBottom: 'borders',
79+
borderLeft: 'borders',
80+
borderWidth: 'borderWidths',
81+
borderStyle: 'borderStyles',
82+
borderRadius: 'radii',
83+
borderTopRightRadius: 'radii',
84+
borderTopLeftRadius: 'radii',
85+
borderBottomRightRadius: 'radii',
86+
borderBottomLeftRadius: 'radii',
87+
borderTopWidth: 'borderWidths',
88+
borderTopColor: 'colors',
89+
borderTopStyle: 'borderStyles',
90+
borderTopLeftRadius: 'radii',
91+
borderTopRightRadius: 'radii',
92+
borderBottomWidth: 'borderWidths',
93+
borderBottomColor: 'colors',
94+
borderBottomStyle: 'borderStyles',
95+
borderBottomLeftRadius: 'radii',
96+
borderBottomRightRadius: 'radii',
97+
borderLeftWidth: 'borderWidths',
98+
borderLeftColor: 'colors',
99+
borderLeftStyle: 'borderStyles',
100+
borderRightWidth: 'borderWidths',
101+
borderRightColor: 'colors',
102+
borderRightStyle: 'borderStyles',
103+
outlineColor: 'colors',
104+
boxShadow: 'shadows',
105+
textShadow: 'shadows',
106+
zIndex: 'zIndices',
107+
width: 'sizes',
108+
minWidth: 'sizes',
109+
maxWidth: 'sizes',
110+
height: 'sizes',
111+
minHeight: 'sizes',
112+
maxHeight: 'sizes',
113+
flexBasis: 'sizes',
114+
size: 'sizes',
115+
// svg
116+
fill: 'colors',
117+
stroke: 'colors',
118+
}
119+
120+
const positiveOrNegative = (scale, value) => {
121+
if (typeof value !== 'number' || value >= 0) {
122+
return get(scale, value, value)
123+
}
124+
const absolute = Math.abs(value)
125+
const n = get(scale, absolute, absolute)
126+
if (typeof n === 'string') return '-' + n
127+
return n * -1
128+
}
129+
130+
const transforms = [
131+
'margin',
132+
'marginTop',
133+
'marginRight',
134+
'marginBottom',
135+
'marginLeft',
136+
'marginX',
137+
'marginY',
138+
'top',
139+
'bottom',
140+
'left',
141+
'right',
142+
].reduce(
143+
(acc, curr) => ({
144+
...acc,
145+
[curr]: positiveOrNegative,
146+
}),
147+
{}
148+
)
149+
150+
export const responsive = styles => theme => {
151+
const next = {}
152+
const breakpoints = get(theme, 'breakpoints', defaultBreakpoints)
153+
const mediaQueries = [
154+
null,
155+
...breakpoints.map(n => `@media screen and (min-width: ${n})`),
156+
]
157+
158+
for (const key in styles) {
159+
const value =
160+
typeof styles[key] === 'function' ? styles[key](theme) : styles[key]
161+
162+
if (value == null) continue
163+
if (!Array.isArray(value)) {
164+
next[key] = value
165+
continue
166+
}
167+
for (let i = 0; i < value.slice(0, mediaQueries.length).length; i++) {
168+
const media = mediaQueries[i]
169+
if (value[i] == null) continue
170+
if (!media) {
171+
next[key] = value[i]
172+
continue
173+
}
174+
next[media] = next[media] || {}
175+
next[media][key] = value[i]
176+
}
177+
}
178+
179+
return next
180+
}
181+
182+
export const css = args => (props = {}) => {
183+
const theme = { ...defaultTheme, ...(props.theme || props) }
184+
let result = {}
185+
const obj = typeof args === 'function' ? args(theme) : args
186+
const styles = responsive(obj)(theme)
187+
188+
for (const key in styles) {
189+
const x = styles[key]
190+
const val = typeof x === 'function' ? x(theme) : x
191+
192+
if (key === 'variant') {
193+
const variant = css(get(theme, val))(theme)
194+
result = { ...result, ...variant }
195+
continue
196+
}
197+
198+
if (val && typeof val === 'object') {
199+
result[key] = css(val)(theme)
200+
continue
201+
}
202+
203+
const prop = get(aliases, key, key)
204+
const scaleName = get(scales, prop)
205+
const scale = get(theme, scaleName, get(theme, prop, {}))
206+
const transform = get(transforms, prop, get)
207+
const value = transform(scale, val, val)
208+
209+
if (multiples[prop]) {
210+
const dirs = multiples[prop]
211+
212+
for (let i = 0; i < dirs.length; i++) {
213+
result[dirs[i]] = value
214+
}
215+
} else {
216+
result[prop] = value
217+
}
218+
}
219+
220+
return result
221+
}
222+
223+
export default css

0 commit comments

Comments
 (0)