@symbo.ls/scratch 0.2.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/package.json +51 -0
- package/src/config/color.js +10 -0
- package/src/config/document.js +10 -0
- package/src/config/font-family.js +16 -0
- package/src/config/font.js +12 -0
- package/src/config/index.js +17 -0
- package/src/config/media.js +0 -0
- package/src/config/responsive.js +13 -0
- package/src/config/sequence.js +24 -0
- package/src/config/spacing.js +66 -0
- package/src/config/theme.js +20 -0
- package/src/config/timing.js +5 -0
- package/src/config/typography.js +39 -0
- package/src/config/unit.js +5 -0
- package/src/factory.js +6 -0
- package/src/index.js +11 -0
- package/src/methods/create.js +9 -0
- package/src/methods/generate.js +40 -0
- package/src/methods/index.js +3 -0
- package/src/methods/inject.js +5 -0
- package/src/methods/set.js +235 -0
- package/src/reset.js +35 -0
- package/src/tests/index.js +4 -0
- package/src/utils/index.js +292 -0
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Φ / Scratch framework
|
|
2
|
+
Scratch is CSS framework and methodology to build web, mobile and TV applications with one code base. Using it means to follow some practices to manage design system and make configuration based cascading styles.
|
|
3
|
+
|
|
4
|
+
[](https://badge.fury.io/js/%40rackai%2Fscratch)
|
|
5
|
+
|
|
6
|
+
### TODO:
|
|
7
|
+
- [ ] Generate CSS variables
|
|
8
|
+
- [ ] Accessibility (WCAG) automated tests
|
|
9
|
+
- [ ] Scratch on `node`
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@symbo.ls/scratch",
|
|
3
|
+
"description": "Φ / CSS framework and methodology.",
|
|
4
|
+
"author": "Symbols",
|
|
5
|
+
"version": "0.2.26",
|
|
6
|
+
"files": [
|
|
7
|
+
"src"
|
|
8
|
+
],
|
|
9
|
+
"repository": "https://github.com/rackai/scratch",
|
|
10
|
+
"main": "src/index.js",
|
|
11
|
+
"publishConfig": {},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"color-contrast-checker": "^1.5.0"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"standard": "npx standard \"src/**/*.js\"",
|
|
17
|
+
"test": "yarn standard && jest --coverage --coverageReporters=text-lcov | coveralls",
|
|
18
|
+
"test-watch": "jest --watch",
|
|
19
|
+
"bump": "npx np"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@babel/core": "^7.10.4",
|
|
23
|
+
"@babel/preset-env": "^7.10.4",
|
|
24
|
+
"babel-eslint": "^10.0.3",
|
|
25
|
+
"babel-jest": "^27.0.2",
|
|
26
|
+
"babel-preset-env": "^1.7.0",
|
|
27
|
+
"coveralls": "^3.0.5",
|
|
28
|
+
"eslint": "^7.12.1",
|
|
29
|
+
"eslint-plugin-jest": "^24.1.0",
|
|
30
|
+
"jest": "^27.0.6",
|
|
31
|
+
"nodemon": "^2.0.6",
|
|
32
|
+
"np": "^7.2.0",
|
|
33
|
+
"parcel-bundler": "^1.12.4",
|
|
34
|
+
"standard": "^16.0.1"
|
|
35
|
+
},
|
|
36
|
+
"jest": {
|
|
37
|
+
"collectCoverageFrom": [
|
|
38
|
+
"src/**/*.js"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"standard": {
|
|
42
|
+
"parser": "babel-eslint",
|
|
43
|
+
"env": [
|
|
44
|
+
"jest"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"browserslist": [
|
|
48
|
+
"> 1%",
|
|
49
|
+
"ie >= 9"
|
|
50
|
+
]
|
|
51
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { getFontFace } from '../utils'
|
|
4
|
+
|
|
5
|
+
var defaultFont = { // eslint-disable-line
|
|
6
|
+
name: '',
|
|
7
|
+
family: '',
|
|
8
|
+
type: ''
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const FONT_FAMILY = {}
|
|
12
|
+
export const FONT_FAMILY_TYPES = {
|
|
13
|
+
serif: 'Helvetica, Arial, sans-serif, --system-default',
|
|
14
|
+
'sans-serif': 'Times New Roman, Georgia, serif, --system-default'
|
|
15
|
+
}
|
|
16
|
+
export const FONT_FACE = getFontFace(FONT_FAMILY)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
export * from './sequence'
|
|
4
|
+
export * from './unit'
|
|
5
|
+
|
|
6
|
+
export * from './typography'
|
|
7
|
+
export * from './font'
|
|
8
|
+
export * from './font-family'
|
|
9
|
+
|
|
10
|
+
export * from './spacing'
|
|
11
|
+
|
|
12
|
+
export * from './color'
|
|
13
|
+
export * from './theme'
|
|
14
|
+
|
|
15
|
+
export * from './responsive'
|
|
16
|
+
|
|
17
|
+
export * from './document'
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
export const SEQUENCE = {
|
|
4
|
+
'minor-second': 1.067,
|
|
5
|
+
'major-second': 1.125,
|
|
6
|
+
'minor-third': 1.2,
|
|
7
|
+
'major-third': 1.25,
|
|
8
|
+
'perfect-fourth': 1.333,
|
|
9
|
+
'augmented-fourth': 1.414,
|
|
10
|
+
'perfect-fifth': 1.5,
|
|
11
|
+
'minor-sixth': 1.6,
|
|
12
|
+
phi: 1.618, // golden-ratio
|
|
13
|
+
'major-sixth': 1.667,
|
|
14
|
+
'square-root-3': 1.732, // theodorus
|
|
15
|
+
'minor-seventh': 1.778,
|
|
16
|
+
'major-seventh': 1.875,
|
|
17
|
+
octave: 2,
|
|
18
|
+
'square-root-5': 2.23, // pythagoras
|
|
19
|
+
'major-tenth': 2.5,
|
|
20
|
+
'major-eleventh': 2.667,
|
|
21
|
+
'major-twelfth': 3,
|
|
22
|
+
pi: 3.14, // archimedes
|
|
23
|
+
'double-octave': 4
|
|
24
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { SEQUENCE, TYPOGRAPHY } from '.'
|
|
4
|
+
import { Arrayize, fallBack, generateSequence } from '../utils'
|
|
5
|
+
|
|
6
|
+
const defaultProps = {
|
|
7
|
+
base: TYPOGRAPHY.base,
|
|
8
|
+
type: 'spacing',
|
|
9
|
+
ratio: SEQUENCE['phi'],
|
|
10
|
+
range: [-5, +7],
|
|
11
|
+
subSequence: true,
|
|
12
|
+
sequence: {},
|
|
13
|
+
scales: {}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
generateSequence(defaultProps)
|
|
17
|
+
|
|
18
|
+
const getSequence = (props) => {
|
|
19
|
+
if (!props) return
|
|
20
|
+
const hasGenerated = Object.keys(props.sequence).length > 0
|
|
21
|
+
return hasGenerated ? props : generateSequence(props)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const mapSpacing = (val, property = 'padding', props) => {
|
|
25
|
+
const prefix = '--spacing-'
|
|
26
|
+
|
|
27
|
+
const generatedSequence = getSequence(props)
|
|
28
|
+
const type = (generatedSequence || defaultProps).sequence
|
|
29
|
+
|
|
30
|
+
const stack = Arrayize(val)
|
|
31
|
+
if (!stack) return
|
|
32
|
+
|
|
33
|
+
const length = stack.length
|
|
34
|
+
|
|
35
|
+
const wrapFallBack = (prop, i) => fallBack({
|
|
36
|
+
type,
|
|
37
|
+
prop,
|
|
38
|
+
val: stack[i],
|
|
39
|
+
prefix
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
if (length === 2) {
|
|
43
|
+
return [
|
|
44
|
+
wrapFallBack(property + 'Block', 0),
|
|
45
|
+
wrapFallBack(property + 'Inline', 1)
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
if (length === 3) {
|
|
49
|
+
return [
|
|
50
|
+
wrapFallBack(property + 'BlockStart', 0),
|
|
51
|
+
wrapFallBack(property + 'Inline', 1),
|
|
52
|
+
wrapFallBack(property + 'BlockEnd', 2)
|
|
53
|
+
]
|
|
54
|
+
} else if (length === 4) {
|
|
55
|
+
return [
|
|
56
|
+
wrapFallBack(property + 'BlockStart', 0),
|
|
57
|
+
wrapFallBack(property + 'InlineStart', 3),
|
|
58
|
+
wrapFallBack(property + 'BlockEnd', 2),
|
|
59
|
+
wrapFallBack(property + 'InlineEnd', 1)
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return fallBack({ type, prop: property, val, prefix })
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const SPACING = defaultProps
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const themeA = { // eslint-disable-line no-unused-vars
|
|
4
|
+
text: 'blue',
|
|
5
|
+
background: 'white',
|
|
6
|
+
border: 'black', // .opacity(0.2),
|
|
7
|
+
active: {},
|
|
8
|
+
helpers: [],
|
|
9
|
+
themes: {},
|
|
10
|
+
inverse: {} // schemeAInverse
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const THEME = {}
|
|
14
|
+
|
|
15
|
+
export const returnSubThemeOrDefault = (orig, theme) => {
|
|
16
|
+
if (!orig) return
|
|
17
|
+
if (orig.themes && orig.themes[theme]) return orig.themes[theme]
|
|
18
|
+
if (orig[theme]) return [orig, orig[theme]]
|
|
19
|
+
return orig
|
|
20
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { SEQUENCE } from '.'
|
|
4
|
+
import { fallBack, generateSequence, findHeadings } from '../utils'
|
|
5
|
+
|
|
6
|
+
const defaultProps = {
|
|
7
|
+
default: 16,
|
|
8
|
+
base: 16,
|
|
9
|
+
type: 'font-size',
|
|
10
|
+
ratio: SEQUENCE['minor-third'],
|
|
11
|
+
range: [-3, +12],
|
|
12
|
+
h1Matches: +6,
|
|
13
|
+
styles: {
|
|
14
|
+
lineHeight: 1.5
|
|
15
|
+
},
|
|
16
|
+
sequence: {},
|
|
17
|
+
scales: {}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
generateSequence(defaultProps)
|
|
21
|
+
|
|
22
|
+
if (defaultProps.h1Matches) {
|
|
23
|
+
var HEADINGS = findHeadings(defaultProps)
|
|
24
|
+
const { styles } = defaultProps
|
|
25
|
+
for (const k in HEADINGS) {
|
|
26
|
+
styles[`h${parseInt(k) + 1}`] = {
|
|
27
|
+
fontSize: `${HEADINGS[k].scaling}em`
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const mapFontSize = val => fallBack({
|
|
33
|
+
type: defaultProps.sequence,
|
|
34
|
+
prop: 'fontSize',
|
|
35
|
+
val,
|
|
36
|
+
prefix: '--font-size-'
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
export const TYPOGRAPHY = defaultProps
|
package/src/factory.js
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { themeMap } from '../config/theme'
|
|
4
|
+
|
|
5
|
+
// var pairAsInvert = (scheme, referenced) => cx(scheme, referenced)
|
|
6
|
+
|
|
7
|
+
var mapThemeCSS = scheme => {
|
|
8
|
+
var str = ''
|
|
9
|
+
for (const prop in scheme) {
|
|
10
|
+
var mappedProp = themeMap[prop]
|
|
11
|
+
var value = scheme[prop]
|
|
12
|
+
if (mappedProp && value) {
|
|
13
|
+
str += `${mappedProp}: ${value}`
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return str
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
var generateTheme = scheme => {
|
|
20
|
+
var { helpers, inverse } = scheme
|
|
21
|
+
|
|
22
|
+
var rule = `
|
|
23
|
+
${mapThemeCSS(scheme)}
|
|
24
|
+
`
|
|
25
|
+
|
|
26
|
+
if (inverse) {
|
|
27
|
+
rule += `&.inverse { ${inverse} }`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (helpers) {
|
|
31
|
+
for (var prop in helpers) {
|
|
32
|
+
var value = helpers[prop]
|
|
33
|
+
rule += `.${prop} { ${value} }`
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return rule
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default { generateTheme }
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import CONFIG, { CSS_VARS } from '../factory'
|
|
4
|
+
// import { FONT, FONT_FAMILY_TYPES } from '../config'
|
|
5
|
+
import {
|
|
6
|
+
isArray,
|
|
7
|
+
colorStringToRgbaArray,
|
|
8
|
+
isObject,
|
|
9
|
+
isString,
|
|
10
|
+
isObjectLike,
|
|
11
|
+
getColorShade,
|
|
12
|
+
hexToRgbArray,
|
|
13
|
+
rgbArrayToHex,
|
|
14
|
+
getDefaultOrFirstKey,
|
|
15
|
+
getFontFaceEach
|
|
16
|
+
} from '../utils'
|
|
17
|
+
|
|
18
|
+
const setColor = (val, key) => {
|
|
19
|
+
const [r, g, b, a = 1] = colorStringToRgbaArray(val.value || val)
|
|
20
|
+
const alpha = parseFloat(a.toFixed(2))
|
|
21
|
+
const CSSVar = `--color-${key}`
|
|
22
|
+
const rgb = `${r}, ${g}, ${b}`
|
|
23
|
+
const value = `rgba(${rgb}, ${alpha})`
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
var: CSSVar,
|
|
27
|
+
rgb,
|
|
28
|
+
alpha,
|
|
29
|
+
value
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const setGradient = (val, key) => {
|
|
34
|
+
const CSSVar = `--gradient-${key}`
|
|
35
|
+
return {
|
|
36
|
+
var: CSSVar,
|
|
37
|
+
value: val.value || val
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const getColor = value => {
|
|
42
|
+
if (!isString(value)) return console.warn(value, '- type for color is not valid')
|
|
43
|
+
|
|
44
|
+
const [name, alpha, tone] = isArray(value) ? value : value.split(' ')
|
|
45
|
+
const { COLOR, GRADIENT } = CONFIG
|
|
46
|
+
const val = COLOR[name] || GRADIENT[name]
|
|
47
|
+
|
|
48
|
+
if (!val) {
|
|
49
|
+
console.warn('Can\'t find color', name)
|
|
50
|
+
return value
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// TODO: support variables
|
|
54
|
+
// if (alpha) return `rgba(var(${val[shade || ''].var}), ${modifier})`
|
|
55
|
+
|
|
56
|
+
let rgb = val.rgb
|
|
57
|
+
if (rgb) {
|
|
58
|
+
if (tone) {
|
|
59
|
+
if (!val[tone]) {
|
|
60
|
+
const toHex = rgbArrayToHex(rgb.split(', '))
|
|
61
|
+
rgb = hexToRgbArray(getColorShade(toHex, tone)).join(', ')
|
|
62
|
+
val[tone] = { rgb, var: `${val.var}-${tone}` }
|
|
63
|
+
} else rgb = val[tone].rgb
|
|
64
|
+
}
|
|
65
|
+
if (alpha) return `rgba(${val.rgb}, ${alpha})`
|
|
66
|
+
return `rgb(${rgb})`
|
|
67
|
+
} else return val.value
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const setThemeValue = theme => {
|
|
71
|
+
const value = {}
|
|
72
|
+
const { state, variants, helpers, ...rest } = theme
|
|
73
|
+
const keys = Object.keys(rest)
|
|
74
|
+
keys.map(key => {
|
|
75
|
+
const conditions = ['color', 'Color', 'background', 'border']
|
|
76
|
+
const isColor = conditions.some(k => key.includes(k))
|
|
77
|
+
return (value[key] = isColor ? getColor(theme[key]) : theme[key])
|
|
78
|
+
})
|
|
79
|
+
return value
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const getThemeValue = theme => {
|
|
83
|
+
if (theme.value) return theme.value
|
|
84
|
+
theme.value = setThemeValue(theme)
|
|
85
|
+
return theme.value
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const getTheme = value => {
|
|
89
|
+
const { THEME } = CONFIG
|
|
90
|
+
if (isObjectLike(value) && value[1]) {
|
|
91
|
+
const themeName = value[0]
|
|
92
|
+
const subThemeName = value[1]
|
|
93
|
+
const { helpers, variants, state } = THEME[themeName]
|
|
94
|
+
if (variants && variants[subThemeName]) return getThemeValue(variants[subThemeName])
|
|
95
|
+
if (helpers && helpers[subThemeName]) return getThemeValue(helpers[subThemeName])
|
|
96
|
+
if (state && state[subThemeName]) return getThemeValue(state[subThemeName])
|
|
97
|
+
} else if (isObject(value)) return setThemeValue(value)
|
|
98
|
+
else if (isString(value) && THEME[value]) return getThemeValue(THEME[value])
|
|
99
|
+
console.warn('Can\'t find theme', value)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const setPrefersScheme = (theme, key, variant, themeValue) => {
|
|
103
|
+
const result = getTheme(variant)
|
|
104
|
+
themeValue[`@media (prefers-color-scheme: ${key})`] = result
|
|
105
|
+
if (isObject(variant) && !variant.value) variant.value = result
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const setInverseTheme = (theme, variant, value) => {
|
|
109
|
+
if (isObject(variant)) {
|
|
110
|
+
theme.variants.inverse.value = setThemeValue(variant)
|
|
111
|
+
} else if (variant === true) {
|
|
112
|
+
const { color, background } = value
|
|
113
|
+
theme.variants.inverse = {
|
|
114
|
+
value: {
|
|
115
|
+
color: background,
|
|
116
|
+
background: color
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const goThroughHelpers = (theme, value) => {
|
|
123
|
+
const { helpers } = theme
|
|
124
|
+
if (!helpers) return
|
|
125
|
+
const keys = Object.keys(helpers)
|
|
126
|
+
keys.map(key => {
|
|
127
|
+
const helper = helpers[key]
|
|
128
|
+
if (isString(helper)) helpers[key] = CONFIG.THEME[helper]
|
|
129
|
+
else getThemeValue(helpers[key])
|
|
130
|
+
return theme
|
|
131
|
+
})
|
|
132
|
+
return theme
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const goThroughVariants = (theme, value) => {
|
|
136
|
+
const { variants } = theme
|
|
137
|
+
if (!variants) return
|
|
138
|
+
const keys = Object.keys(variants)
|
|
139
|
+
keys.map(key => {
|
|
140
|
+
const variant = variants[key]
|
|
141
|
+
if (key === 'dark' || key === 'light') setPrefersScheme(theme, key, variant, value)
|
|
142
|
+
if (key === 'inverse') setInverseTheme(theme, variant, value)
|
|
143
|
+
return theme
|
|
144
|
+
})
|
|
145
|
+
return theme
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const setPseudo = (theme, key, variant, themeValue) => {
|
|
149
|
+
const result = getTheme(variant)
|
|
150
|
+
themeValue[`&:${key}`] = result
|
|
151
|
+
if (isObject(variant) && !variant.value) variant.value = result
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const goThroughInteractiveStates = (theme, value) => {
|
|
155
|
+
const { state } = theme
|
|
156
|
+
if (!state) return
|
|
157
|
+
const keys = Object.keys(state)
|
|
158
|
+
keys.map(key => {
|
|
159
|
+
const variant = state[key]
|
|
160
|
+
setPseudo(theme, key, variant, value)
|
|
161
|
+
return theme
|
|
162
|
+
})
|
|
163
|
+
return theme
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const setTheme = (val, key) => {
|
|
167
|
+
const { state, variants, helpers } = val
|
|
168
|
+
const value = setThemeValue(val, key)
|
|
169
|
+
const CSSvar = `--theme-${key}`
|
|
170
|
+
|
|
171
|
+
goThroughInteractiveStates(val, value)
|
|
172
|
+
goThroughVariants(val, value)
|
|
173
|
+
goThroughHelpers(val, value)
|
|
174
|
+
|
|
175
|
+
return { var: CSSvar, value, state, variants, helpers }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const setFont = (val, key) => {
|
|
179
|
+
const CSSvar = `--font-${key}`
|
|
180
|
+
const fontFace = getFontFaceEach(key, val)
|
|
181
|
+
return { var: CSSvar, value: val, fontFace }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const getFontFamily = (LIBRARY, key) => {
|
|
185
|
+
return getDefaultOrFirstKey(LIBRARY, key)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const setFontFamily = (val, key) => {
|
|
189
|
+
const { FONT_FAMILY, FONT_FAMILY_TYPES } = CONFIG
|
|
190
|
+
const { value, type } = val
|
|
191
|
+
if (val.default) FONT_FAMILY.default = key
|
|
192
|
+
|
|
193
|
+
const CSSvar = `--font-family-${key}`
|
|
194
|
+
const str = `${value.join(', ')}, ${FONT_FAMILY_TYPES[type]}`
|
|
195
|
+
return { var: CSSvar, value: str, arr: value, type }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export const SETTERS = {
|
|
199
|
+
color: setColor,
|
|
200
|
+
gradient: setGradient,
|
|
201
|
+
font: setFont,
|
|
202
|
+
font_family: setFontFamily,
|
|
203
|
+
theme: setTheme
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
*
|
|
208
|
+
* @param {Object} FACTORY_NAME Defines which factory it goes to
|
|
209
|
+
* @param {*} value Value of the property
|
|
210
|
+
* @param {String} key Key, or the name of the property
|
|
211
|
+
* @returns {Object} Factory
|
|
212
|
+
*/
|
|
213
|
+
export const setValue = (FACTORY_NAME, value, key) => {
|
|
214
|
+
const factoryName = FACTORY_NAME.toLowerCase()
|
|
215
|
+
const FACTORY = CONFIG[FACTORY_NAME]
|
|
216
|
+
const result = SETTERS[factoryName](value, key)
|
|
217
|
+
FACTORY[key] = result
|
|
218
|
+
CSS_VARS[result.var] = result.value
|
|
219
|
+
return FACTORY
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export const setEach = (factoryName, props) => {
|
|
223
|
+
const FACTORY_NAME = factoryName.toUpperCase()
|
|
224
|
+
const keys = Object.keys(props)
|
|
225
|
+
keys.map(key => setValue(FACTORY_NAME, props[key], key))
|
|
226
|
+
return CONFIG[FACTORY_NAME]
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export const set = config => {
|
|
230
|
+
const keys = Object.keys(config)
|
|
231
|
+
keys.map(key => setEach(key, config[key]))
|
|
232
|
+
return CONFIG
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export default set
|
package/src/reset.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import * as CONFIG from './config'
|
|
4
|
+
|
|
5
|
+
export const RESET = {
|
|
6
|
+
html: {
|
|
7
|
+
position: 'fixed',
|
|
8
|
+
overflow: 'hidden',
|
|
9
|
+
width: '100%',
|
|
10
|
+
height: '100%',
|
|
11
|
+
top: '0',
|
|
12
|
+
left: '0',
|
|
13
|
+
margin: '0',
|
|
14
|
+
WebkitFontSmoothing: 'antialiased',
|
|
15
|
+
|
|
16
|
+
fontFamily: CONFIG.DOCUMENT.fontFamily,
|
|
17
|
+
|
|
18
|
+
fontSize: CONFIG.TYPOGRAPHY.default / CONFIG.DOCUMENT.fontSize + CONFIG.UNIT.default,
|
|
19
|
+
lineHeight: CONFIG.DOCUMENT.lineHeight
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
body: {
|
|
23
|
+
boxSizing: 'border-box',
|
|
24
|
+
height: '100%'
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
...CONFIG.TYPOGRAPHY.styles,
|
|
28
|
+
|
|
29
|
+
// form elements
|
|
30
|
+
fieldset: {
|
|
31
|
+
border: 0,
|
|
32
|
+
padding: 0,
|
|
33
|
+
margin: 0
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { UNIT } from '../config'
|
|
4
|
+
|
|
5
|
+
export const isString = arg => typeof arg === 'string'
|
|
6
|
+
|
|
7
|
+
export const isArray = arg => Array.isArray(arg)
|
|
8
|
+
|
|
9
|
+
export const isObject = arg => {
|
|
10
|
+
if (arg === null) return false
|
|
11
|
+
return (typeof arg === 'object') && (arg.constructor === Object)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const isObjectLike = arg => {
|
|
15
|
+
if (arg === null) return false
|
|
16
|
+
return (typeof arg === 'object')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const merge = (obj, original) => {
|
|
20
|
+
for (const e in original) {
|
|
21
|
+
const objProp = obj[e]
|
|
22
|
+
const originalProp = original[e]
|
|
23
|
+
if (objProp === undefined) {
|
|
24
|
+
obj[e] = originalProp
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return obj
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const colorStringToRgbaArray = color => {
|
|
31
|
+
if (color === '') return
|
|
32
|
+
if (color.toLowerCase() === 'transparent') return [0, 0, 0, 0]
|
|
33
|
+
|
|
34
|
+
// convert #RGB and #RGBA to #RRGGBB and #RRGGBBAA
|
|
35
|
+
if (color[0] === '#') {
|
|
36
|
+
if (color.length < 7) {
|
|
37
|
+
color = '#' + color[1] + color[1] + color[2] + color[2] + color[3] + color[3] + (color.length > 4 ? color[4] + color[4] : '')
|
|
38
|
+
} return [parseInt(color.substr(1, 2), 16),
|
|
39
|
+
parseInt(color.substr(3, 2), 16),
|
|
40
|
+
parseInt(color.substr(5, 2), 16),
|
|
41
|
+
color.length > 7 ? parseInt(color.substr(7, 2), 16) / 255 : 1]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// convert named colors
|
|
45
|
+
if (color.indexOf('rgb') === -1) {
|
|
46
|
+
// intentionally use unknown tag to lower chances of css rule override with !important
|
|
47
|
+
const elem = document.body.appendChild(document.createElement('fictum'))
|
|
48
|
+
// this flag tested on chrome 59, ff 53, ie9, ie10, ie11, edge 14
|
|
49
|
+
const flag = 'rgb(1, 2, 3)'
|
|
50
|
+
elem.style.color = flag
|
|
51
|
+
// color set failed - some monstrous css rule is probably taking over the color of our object
|
|
52
|
+
if (elem.style.color !== flag) return
|
|
53
|
+
elem.style.color = color
|
|
54
|
+
if (elem.style.color === flag || elem.style.color === '') return // color parse failed
|
|
55
|
+
color = window.getComputedStyle(elem).color
|
|
56
|
+
document.body.removeChild(elem)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// convert 'rgb(R,G,B)' to 'rgb(R,G,B,A)' which looks awful but will pass the regxep below
|
|
60
|
+
if (color.indexOf('rgb') === 0) {
|
|
61
|
+
if (color.indexOf('rgba') === -1) color = `${color}, 1`
|
|
62
|
+
return color.match(/[\.\d]+/g).map(a => +a) // eslint-disable-line
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const mixTwoColors = (colorA, colorB, range = 0.5) => {
|
|
67
|
+
colorA = colorStringToRgbaArray(colorA)
|
|
68
|
+
colorB = colorStringToRgbaArray(colorB)
|
|
69
|
+
return mixTwoRgba(colorA, colorB, range)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const hexToRgb = (hex, alpha = 1) => {
|
|
73
|
+
const [r, g, b] = hex.match(/\w\w/g).map(x => parseInt(x, 16))
|
|
74
|
+
return `rgb(${r},${g},${b})`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const hexToRgbArray = (hex, alpha = 1) => {
|
|
78
|
+
const [r, g, b] = hex.match(/\w\w/g).map(x => parseInt(x, 16))
|
|
79
|
+
return [r, g, b]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const rgbToHex = (r, g, b) => {
|
|
83
|
+
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const rgbArrayToHex = ([r, g, b]) => {
|
|
87
|
+
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const hexToRgba = (hex, alpha = 1) => {
|
|
91
|
+
const [r, g, b] = hex.match(/\w\w/g).map(x => parseInt(x, 16))
|
|
92
|
+
return `rgba(${r},${g},${b},${alpha})`
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const mixTwoRgb = (colorA, colorB, range = 0.5) => {
|
|
96
|
+
const arr = []
|
|
97
|
+
for (let i = 0; i < 3; i++) {
|
|
98
|
+
arr[i] = Math.round(
|
|
99
|
+
colorA[i] + (
|
|
100
|
+
(colorB[i] - colorA[i]) * range
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
return `rgb(${arr})`
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const getColorShade = (col, amt) => {
|
|
108
|
+
const num = parseInt(col, 16)
|
|
109
|
+
|
|
110
|
+
let r = (num >> 16) + amt
|
|
111
|
+
|
|
112
|
+
if (r > 255) r = 255
|
|
113
|
+
else if (r < 0) r = 0
|
|
114
|
+
|
|
115
|
+
let b = ((num >> 8) & 0x00FF) + amt
|
|
116
|
+
|
|
117
|
+
if (b > 255) b = 255
|
|
118
|
+
else if (b < 0) b = 0
|
|
119
|
+
|
|
120
|
+
let g = (num & 0x0000FF) + amt
|
|
121
|
+
|
|
122
|
+
if (g > 255) g = 255
|
|
123
|
+
else if (g < 0) g = 0
|
|
124
|
+
|
|
125
|
+
return (g | (b << 8) | (r << 16)).toString(16)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export const mixTwoRgba = (colorA, colorB, range = 0.5) => {
|
|
129
|
+
const arr = []
|
|
130
|
+
for (let i = 0; i < 4; i++) {
|
|
131
|
+
const round = (i === 3) ? x => x : Math.round
|
|
132
|
+
arr[i] = round(
|
|
133
|
+
(colorA[i] + (
|
|
134
|
+
(colorB[i] - colorA[i]) * range
|
|
135
|
+
))
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
return `rgba(${arr})`
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const opacify = (color, opacity) => {
|
|
142
|
+
const arr = colorStringToRgbaArray(color)
|
|
143
|
+
if (!arr) return console.warn(color + 'color is not rgba')
|
|
144
|
+
arr[3] = opacity
|
|
145
|
+
return `rgba(${arr})`
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export const getDefaultOrFirstKey = (LIBRARY, key) => {
|
|
149
|
+
if (LIBRARY[key]) return LIBRARY[key].value
|
|
150
|
+
if (LIBRARY.default) return LIBRARY[LIBRARY.default].value
|
|
151
|
+
return LIBRARY[Object.keys(LIBRARY)[0]].value
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export const getFontFormat = url => url.split(/[#?]/)[0].split('.').pop().trim()
|
|
155
|
+
|
|
156
|
+
export const setCustomFont = (name, weight, url) => `@font-face {
|
|
157
|
+
font-family: '${name}';
|
|
158
|
+
font-style: normal;
|
|
159
|
+
font-weight: ${weight};
|
|
160
|
+
src: url('${url}') format('${getFontFormat(url)}');
|
|
161
|
+
}`
|
|
162
|
+
// src: url('${url}') format('${getFontFormat(url)}');
|
|
163
|
+
|
|
164
|
+
export const getFontFaceEach = (name, weightsObject) => {
|
|
165
|
+
const keys = Object.keys(weightsObject)
|
|
166
|
+
const weightsJoint = keys.map(key => {
|
|
167
|
+
const { fontWeight, url } = weightsObject[key]
|
|
168
|
+
return setCustomFont(name, fontWeight, url)
|
|
169
|
+
})
|
|
170
|
+
return weightsJoint.join('\n')
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const getFontFace = LIBRARY => {
|
|
174
|
+
const keys = Object.keys(LIBRARY)
|
|
175
|
+
const fontsJoint = keys.map(key => getFontFaceEach(key, LIBRARY[key].value))
|
|
176
|
+
return fontsJoint.join('\n')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export const numToLetterMap = {
|
|
180
|
+
'-6': 'U',
|
|
181
|
+
'-5': 'V',
|
|
182
|
+
'-4': 'W',
|
|
183
|
+
'-3': 'X',
|
|
184
|
+
'-2': 'Y',
|
|
185
|
+
'-1': 'Z',
|
|
186
|
+
0: 'A',
|
|
187
|
+
1: 'B',
|
|
188
|
+
2: 'C',
|
|
189
|
+
3: 'D',
|
|
190
|
+
4: 'E',
|
|
191
|
+
5: 'F',
|
|
192
|
+
6: 'G',
|
|
193
|
+
7: 'H',
|
|
194
|
+
8: 'I',
|
|
195
|
+
9: 'J',
|
|
196
|
+
10: 'K',
|
|
197
|
+
11: 'L',
|
|
198
|
+
12: 'M',
|
|
199
|
+
13: 'N',
|
|
200
|
+
14: 'O',
|
|
201
|
+
15: 'P'
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const setSequenceValue = ({ key, variable, value, scaling, state, index }) => {
|
|
205
|
+
state.sequence[variable] = {
|
|
206
|
+
key,
|
|
207
|
+
decimal: Math.round(value * 100) / 100,
|
|
208
|
+
val: Math.round(value),
|
|
209
|
+
scaling,
|
|
210
|
+
index
|
|
211
|
+
}
|
|
212
|
+
state.scales[variable] = scaling
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export const generateSubSequence = ({ key, base, value, ratio, variable, state }) => {
|
|
216
|
+
const next = value * ratio
|
|
217
|
+
const smallscale = (next - value) / ratio
|
|
218
|
+
|
|
219
|
+
const valueRounded = Math.round(value)
|
|
220
|
+
const nextRounded = Math.round(next)
|
|
221
|
+
const diffRounded = nextRounded - valueRounded
|
|
222
|
+
|
|
223
|
+
let arr = []
|
|
224
|
+
const first = next - smallscale
|
|
225
|
+
const second = value + smallscale
|
|
226
|
+
const middle = (first + second) / 2
|
|
227
|
+
if (diffRounded > 100) arr = [first, middle, second]
|
|
228
|
+
else arr = [first, second]
|
|
229
|
+
// else if (diffRounded > 2) arr = [first, second]
|
|
230
|
+
// else if (diffRounded > 1) arr = [middle]
|
|
231
|
+
|
|
232
|
+
arr.map((v, k) => {
|
|
233
|
+
const scaling = Math.round(v / base * 1000) / 1000
|
|
234
|
+
const newVar = variable + (k + 1)
|
|
235
|
+
|
|
236
|
+
return setSequenceValue({ key: key + (k + 1), variable: newVar, value: v, scaling, state })
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export const generateSequence = ({ type, base, ratio, range, subSequence, ...state }) => {
|
|
241
|
+
const n = Math.abs(range[0]) + Math.abs(range[1])
|
|
242
|
+
const prefix = '--' + type + '-'
|
|
243
|
+
for (let i = 0; i <= n; i++) {
|
|
244
|
+
const key = range[1] - i
|
|
245
|
+
const letterKey = numToLetterMap[key]
|
|
246
|
+
const value = base * Math.pow(ratio, key)
|
|
247
|
+
const scaling = Math.round(value / base * 1000) / 1000
|
|
248
|
+
const variable = prefix + letterKey
|
|
249
|
+
|
|
250
|
+
setSequenceValue({ key: letterKey, variable, value, scaling, state, index: key })
|
|
251
|
+
|
|
252
|
+
if (subSequence) generateSubSequence({ key: letterKey, base, value, ratio, variable, state })
|
|
253
|
+
}
|
|
254
|
+
return state
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export const fallBack = ({ type, prop, val = 'A', prefix = '--font-size-' }) => {
|
|
258
|
+
if (typeof val !== 'string') console.warn(prop, val, 'is not a string')
|
|
259
|
+
|
|
260
|
+
// const startsWithLetterRegex = /^[a-zA-Z]/i
|
|
261
|
+
const startsWithLetterRegex = /^-?[a-zA-Z]/i
|
|
262
|
+
// const hasLetter = /[A-Za-z]+/.test(val)
|
|
263
|
+
const startsWithLetter = startsWithLetterRegex.test(val)
|
|
264
|
+
if (!startsWithLetter) return ({ [prop]: val })
|
|
265
|
+
|
|
266
|
+
const letterVal = val.toUpperCase()
|
|
267
|
+
const isNegative = letterVal.slice(0, 1) === '-' ? '-' : ''
|
|
268
|
+
const simplyLetterVal = isNegative ? letterVal.slice(1) : letterVal
|
|
269
|
+
|
|
270
|
+
const value = type ? type[prefix + simplyLetterVal] : null
|
|
271
|
+
if (!value) return console.warn('can\'t find', type, prefix + simplyLetterVal, simplyLetterVal)
|
|
272
|
+
|
|
273
|
+
return ({
|
|
274
|
+
[prop]: isNegative + value.val + UNIT.default,
|
|
275
|
+
[prop]: isNegative + value.scaling + 'em'
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export const Arrayize = val => {
|
|
280
|
+
const isString = typeof val === 'string'
|
|
281
|
+
if (isString) return val.split(' ')
|
|
282
|
+
if (isObject(val)) return Object.keys(val).map(v => val[v])
|
|
283
|
+
if (isArray(val)) return val
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export const findHeadings = (TYPOGRAPHY) => {
|
|
287
|
+
const { h1Matches, type, sequence } = TYPOGRAPHY
|
|
288
|
+
return new Array(6).fill(null).map((_, i) => {
|
|
289
|
+
const findLetter = numToLetterMap[h1Matches - i]
|
|
290
|
+
return sequence[`--${type}-${findLetter}`]
|
|
291
|
+
})
|
|
292
|
+
}
|