README
- @amory/style :properties: :header-args: :cache yes :comments no :mkdirp yes :padline yes :results silent :end:
+startup: showall nohideblocks hidestars indent
** Table of Contents :TOC:
- [[#amorystyle][@amory/style]]
- [[#usage][Usage]]
- [[#with-nextjs][With Next.js]]
- [[#pages_appjs][pages/_app.js]]
- [[#pages_documentjs][pages/_document.js]]
- [[#with-nextjs][With Next.js]]
- [[#license][License]]
- [[#source][Source]]
- [[#api][API]]
- [[#create][create]]
- [[#css][css]]
- [[#index][index]]
- [[#style][style]]
- [[#build][Build]]
- [[#getancestors][getAncestors]]
- [[#getblockstring][getBlockString]]
- [[#getclassname][getClassName]]
- [[#getplaceholders][getPlaceholders]]
- [[#getpropertyid][getPropertyId]]
- [[#getselectors][getSelectors]]
- [[#getselectorsstring][getSelectorsString]]
- [[#getstringhash][getStringHash]]
- [[#getstyle][getStyle]]
- [[#getstyles][getStyles]]
- [[#client][Client]]
- [[#canusedom][canUseDom]]
- [[#getstyleelement][getStyleElement]]
- [[#insertrule][insertRule]]
- [[#update][update]]
- [[#updatestyles][updateStyles]]
- [[#parse][Parse]]
- [[#parsefallbacks][parseFallbacks]]
- [[#parsefontface][parseFontFace]]
- [[#parseidentifier][parseIdentifier]]
- [[#parseinput][parseInput]]
- [[#parsekeyframes][parseKeyframes]]
- [[#parsemedia][parseMedia]]
- [[#parsenumbers][parseNumbers]]
- [[#parseplaceholder][parsePlaceholder]]
- [[#parseselectors][parseSelectors]]
- [[#parsetypeselector][parseTypeSelector]]
- [[#parse-1][parse]]
- [[#store][Store]]
- [[#cache][cache]]
- [[#store-1][store]]
- [[#utils][Utils]]
- [[#camelcase][camelCase]]
- [[#debounce][debounce]]
- [[#kebabcase][kebabCase]]
- [[#merge][merge]]
- [[#pubsub][pubSub]]
- [[#topairs][toPairs]]
- [[#api][API]]
- [[#usage][Usage]]
** Usage
*** With Next.js
**** pages/_app.js
+begin_src js
import { getStyles } from '@amory/style' import App, { Container } from 'next/app' import React from 'react'
export default class extends App { componentDidMount () { /* For testing purposes during development */ window.getStyles = getStyles }
render () { const { Component, pageProps } = this.props
return (
<Container>
<Component {... pageProps } />
</Container>
)
} }
+end_src
**** pages/_document.js
+begin_src js
import { getStyles } from "@amory/style" import Document from "next/document" import React from "react"
export default class extends Document { static getInitialProps ({ renderPage }) { const initialProps = renderPage ( (App) => (props) => (<App {...props} />) )
return {
... initialProps,
"styles": (
<React.Fragment>
{initialProps.styles}
<style
dangerouslySetInnerHTML={{ __html: getStyles () }}
data-creator="@amory/style" />
</React.Fragment>
)
}
} }
+end_src
** License
+begin_quote
Copyright 2019 [[https://github.com/ptb][Peter T Bosse II]]
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+end_quote
** Source
*** API
**** create
+HTML:
+begin_src js :tangle src/api/create.js
import { css, toPairs } from "./index.js"
export function create (params = {}) { return toPairs (params).reduce (function (styles, style) { const property = style[0] const value = style[1]
styles[property] = css (value)
return styles
}, {}) }
+end_src
+HTML:
+HTML:
+begin_src js :tangle src/api/create.test.js
import ava from "ava" import { create } from "./create.js" import { css } from "./css.js"
ava ("given undefined arguments", (t) => { const actual = create () const expect = {}
t.deepEqual (actual, expect) })
ava ("given an object with simple declarations", (t) => { const actual = create ({ "banner": { "display": "block", "width": "80%" }, "product": { "color": "#f00", "display": ["block", "flex", "grid"] } })
const expect = { "banner": "drtx9 dtndl", "product": "jk2a9 dr4gk" }
t.deepEqual (actual, expect) })
/* eslint-disable max-lines-per-function */ ava ("given an object with multiple 'fontFamily' declarations", (t) => { const fonts = { "avenir300": { "fontFamily": { "fontFamily": "Avenir", "fontStyle": "normal", "fontWeight": 300, "src": "url(/fonts/avenir-300-light-normal.woff) format(woff)" } }, "avenir300i": { "fontFamily": { "fontFamily": "Avenir", "fontStyle": "italic", "fontWeight": 300, "src": "url(/fonts/avenir-300-light-oblique.woff) format(woff)" } }, "avenir400": { "fontFamily": { "fontFamily": "Avenir", "fontStyle": "normal", "fontWeight": 400, "src": "url(/fonts/avenir-400-book-normal.woff) format(woff)" } }, "avenir400i": { "fontFamily": { "fontFamily": "Avenir", "fontStyle": "italic", "fontWeight": 400, "src": "url(/fonts/avenir-400-book-oblique.woff) format(woff)" } }, "avenir500": { "fontFamily": { "fontFamily": "Avenir", "fontStyle": "normal", "fontWeight": 500, "src": "url(/fonts/avenir-500-roman-normal.woff) format(woff)" } }, "avenir500i": { "fontFamily": { "fontFamily": "Avenir", "fontStyle": "italic", "fontWeight": 500, "src": "url(/fonts/avenir-500-roman-oblique.woff) format(woff)" } }, "avenir600": { "fontFamily": { "fontFamily": "Avenir", "fontStyle": "normal", "fontWeight": 600, "src": "url(/fonts/avenir-600-medium-normal.woff) format(woff)" } }, "avenir600i": { "fontFamily": { "fontFamily": "Avenir", "fontStyle": "italic", "fontWeight": 600, "src": "url(/fonts/avenir-600-medium-oblique.woff) format(woff)" } }, "avenir700": { "fontFamily": { "fontFamily": "Avenir", "fontStyle": "normal", "fontWeight": 700, "src": "url(/fonts/avenir-700-heavy-normal.woff) format(woff)" } }, "avenir700i": { "fontFamily": { "fontFamily": "Avenir", "fontStyle": "italic", "fontWeight": 700, "src": "url(/fonts/avenir-700-heavy-oblique.woff) format(woff)" } }, "avenir800": { "fontFamily": { "fontFamily": "Avenir", "fontStyle": "normal", "fontWeight": 800, "src": "url(/fonts/avenir-800-black-normal.woff) format(woff)" } }, "avenir800i": { "fontFamily": { "fontFamily": "Avenir", "fontStyle": "italic", "fontWeight": 800, "src": "url(/fonts/avenir-800-black-oblique.woff) format(woff)" } } }
const actual1 = create (fonts)
const expect1 = { "avenir300": "c5w4b", "avenir300i": "c59u0", "avenir400": "c5krw", "avenir400i": "c5c5b", "avenir500": "c5ji2", "avenir500i": "c5qt3", "avenir600": "c5ync", "avenir600i": "c5vw5", "avenir700": "c5j0a", "avenir700i": "c5xmd", "avenir800": "c5h5q", "avenir800i": "c5sr5" }
const actual2 = css (fonts.avenir300)
const expect2 = "c5w4b"
t.deepEqual (actual1, expect1) t.deepEqual (actual2, expect2) }) /* eslint-enable max-lines-per-function */
+end_src
+HTML:
**** css
+HTML:
+begin_src js :tangle src/api/css.js
import { cache, getClassName, isArr, merge, parse, update } from "./index.js"
export function css (params) { const input = isArr (params) ? merge (... params) : params
return parse ({ input }) .map (cache) .map (update) .map (getClassName) .filter (Boolean) .join (" ") }
+end_src
+HTML:
+HTML:
+begin_src js :tangle src/api/css.test.js
import ava from "ava" import { store } from "../store/store.js" import { css } from "./css.js"
function strMapToObj (strMap) { const obj = Object.create (null)
for (const [k, v] of strMap) { obj[k] = v }
return obj }
ava ("given undefined arguments", (t) => { const actual = css () const expect = ""
t.deepEqual (actual, expect) })
ava ("given an object with simple declarations", (t) => { const actual1 = css ({ "backgroundColor": "#f00", "display": "block" })
const expect1 = "jt2a9 drtx9"
const actual2 = strMapToObj (store.get (""))
const expect2 = { '[{"background-color":"#f00"}]': { "block": [ { "background-color": "#f00" } ], "emit": true, "identifier": "jt2a9", "input": { "backgroundColor": "#f00" }, "insertRule": true, "media": "", "property": "backgroundColor", "selectors": [[".jt2a9"]], "value": "#f00" }, '[{"display":"block"}]': { "block": [ { "display": "block" } ], "emit": true, "identifier": "drtx9", "input": { "display": "block" }, "insertRule": true, "media": "", "property": "display", "selectors": [[".drtx9"]], "value": "block" } }
t.is (actual1, expect1) t.deepEqual (actual2, expect2) })
ava ("given an array of objects with simple declarations", (t) => { const actual = css ([ { "backgroundColor": "#f00", "display": "block" }, { "backgroundColor": "#0f0" } ])
const expect = "jtz4h drtx9"
t.is (actual, expect) })
+end_src
+HTML:
**** index
+HTML:
+begin_src js :tangle src/api/index.js
export { create } from "./create.js" export { css } from "./css.js" export { getAncestors } from "../build/get-ancestors.js" export { getBlockString } from "../build/get-block-string.js" export { getClassName } from "../build/get-class-name.js" export { getPlaceholders } from "../build/get-placeholders.js" export { getPropertyId } from "../build/get-property-id.js" export { getSelectors } from "../build/get-selectors.js" export { getSelectorsString } from "../build/get-selectors-string.js" export { getStringHash } from "../build/get-string-hash.js" export { getStyle } from "../build/get-style.js" export { getStyles } from "../build/get-styles.js" export { canUseDom } from "../client/can-use-dom.js" export { getStyleElement } from "../client/get-style-element.js" export { insertRule } from "../client/insert-rule.js" export { update } from "../client/update.js" export { updateStyles } from "../client/update-styles.js" export { parse } from "../parse/parse.js" export { parseFallbacks } from "../parse/parse-fallbacks.js" export { parseFontFace } from "../parse/parse-font-face.js" export { parseIdentifier } from "../parse/parse-identifier.js" export { parseInput } from "../parse/parse-input.js" export { parseKeyframes } from "../parse/parse-keyframes.js" export { parseMedia } from "../parse/parse-media.js" export { parseNumbers } from "../parse/parse-numbers.js" export { parsePlaceholder } from "../parse/parse-placeholder.js" export { parseSelectors } from "../parse/parse-selectors.js" export { parseTypeSelector } from "../parse/parse-type-selector.js" export { cache } from "../store/cache.js" export { store } from "../store/store.js" export { camelCase } from "../utils/camel-case.js" export { debounce } from "../utils/debounce.js" export { kebabCase } from "../utils/kebab-case.js" export { canMerge, cloneObj, emptyObj, isArr, isDef, isNum, isObj, merge, mergeArr, mergeObj } from "../utils/merge.js" export { pubSub } from "../utils/pub-sub.js" export { toPairs } from "../utils/to-pairs.js"
+end_src
+HTML:
**** style
+HTML:
+begin_src js :tangle src/api/style.js
export { create } from "./create.js" export { css } from "./css.js" export { getStyles } from "../build/get-styles.js" export { merge } from "../utils/merge.js"
+end_src
+HTML:
*** Build
**** getAncestors
+HTML:
+begin_src js :tangle src/build/get-ancestors.js
export function getAncestors (ancestors = [], selectors = []) { return selectors.reduce (function (results, selector) { if (ancestors.length) { const index = selector.indexOf ("&")
ancestors.forEach (function (ancestor) {
results.push (
index < 0
? ancestor.concat (" ", selector)
: selector
.slice (0, index)
.concat (ancestor, selector.slice (index + 1))
)
})
return results
}
return results.concat ([selector])
}, []) }
+end_src
+HTML:
+HTML:
+begin_src js :tangle src/build/get-ancestors.test.js
import ava from "ava" import { getAncestors } from "./get-ancestors.js"
ava ("given undefined arguments", (t) => { const actual = getAncestors ()
const expect = []
t.deepEqual (actual, expect) })
ava ("given an array of selectors without ancestors", (t) => { const actual = getAncestors ([], [["#root"]])
const expect = [["#root"]]
t.deepEqual (actual, expect) })
ava ("given an array of selectors with implied ancestor location", (t) => { const actual = getAncestors ([["#root"]], [["#body"]])
const expect = [["#root", " ", "#body"]]
t.deepEqual (actual, expect) })
ava ("given an array of selectors with defined ancestor prefix", (t) => { const actual = getAncestors ([["#root"]], [["&", " ", "#body"]])
const expect = [["#root", " ", "#body"]]
t.deepEqual (actual, expect) })
ava ("given an array of selectors with defined ancestor suffix", (t) => { const actual = getAncestors ([["#root"]], [["#body", " ", "&"]])
const expect = [["#body", " ", "#root"]]
t.deepEqual (actual, expect) })
ava ("given an array of selectors with defined ancestor middle", (t) => { const actual = getAncestors ( [["#root"]], [["#body", " ", "&", " ", "%thing"]] )
const expect = [["#body", " ", "#root", " ", "%thing"]]
t.deepEqual (actual, expect) })
ava ("given an array of selectors with array of ancestors", (t) => { const actual = getAncestors ( [["#root", "#body"], ["%test"]], [["#more", ">", "%stuff"], ["#thing", " ", "&", "+", "%thing"]] )
const expect = [ ["#root", "#body", " ", "#more", ">", "%stuff"], ["%test", " ", "#more", ">", "%stuff"], ["#thing", " ", "#root", "#body", "+", "%thing"], ["#thing", " ", "%test", "+", "%thing"] ]
t.deepEqual (actual, expect) })
+end_src
+HTML:
**** getBlockString
+HTML:
+begin_src js :tangle src/build/get-block-string.js
import { isObj, kebabCase, toPairs } from "../api/index.js"
export function getBlockString (params = {}) { const block = params.block || []
let sep = ";"
return block .map (function (rule) { return toPairs (rule).map (function (style) { const property = style[0] const value = style[1]
if (isObj (value)) {
const a = toPairs (value)
.map (function (b) {
return kebabCase (b[0]).concat (":", b[1])
})
.join (";")
sep = ""
return "".concat (property, "{", a, "}")
}
return "".concat (property, ":", value)
})
})
.join (sep)
}
+end_src
+HTML:
+HTML:
+begin_src js :tangle src/build/get-block-string.test.js
import ava from "ava" import { getBlockString } from "./get-block-string.js"
ava ("given undefined arguments", (t) => { const actual = getBlockString ()
const expect = ""
t.is (actual, expect) })
ava ("given a block with simple property and value", (t) => { const actual = getBlockString ({ "block": [ { "background-color": "#f00" } ] })
const expect = "background-color:#f00"
t.is (actual, expect) })
ava ("given a block with fallback properties and value", (t) => { const actual = getBlockString ({ "block": [ { "background-color": "#f00" }, { "background-color": "rgba(255, 0, 0, 0.9)" } ] })
const expect = "background-color:#f00;background-color:rgba(255, 0, 0, 0.9)"
t.is (actual, expect) })
ava ("given a block with keyframes object", (t) => { const actual = getBlockString ({ "block": [ { "0%": { "backgroundColor": "#f00", "opacity": 0 } }, { "100%": { "backgroundColor": "#0f0", "opacity": 1 } } ] })
const expect = "0%{background-color:#f00;opacity:0}100%{background-color:#0f0;opacity:1}"
t.is (actual, expect) })
+end_src
+HTML:
**** getClassName
+HTML:
+begin_src js :tangle src/build/get-class-name.js
export function getClassName (params = {}) { const emit = params.emit const identifier = params.identifier
return emit ? identifier : null }
+end_src
+HTML:
+HTML:
+begin_src js :tangle src/build/get-class-name.test.js
import ava from "ava" import { getClassName } from "./get-class-name.js"
ava ("given undefined arguments", (t) => { const actual = getClassName ()
const expect = null
t.deepEqual (actual, expect) })
ava ("given an object with identifier and emit true", (t) => { const actual = getClassName ({ "emit": true, "identifier": "jtz4h", "property": "backgroundColor", "selectors": [[".jtz4h"]], "value": "#0f0" })
const expect = "jtz4h"
t.deepEqual (actual, expect) })
ava ("given an object with identifier and emit false", (t) => { const actual = getClassName ({ "block": [ { "src": "url('/fonts/font.woff2') format ('woff2'), url('/fonts/font.woff') format ('woff')" }, { "font-family": "c5xq1" } ], "emit": false, "identifier": "c5xq1", "input": { "fontFamily": { "src": "url('/fonts/font.woff2') format ('woff2'), url('/fonts/font.woff') format ('woff')" } }, "media": "", "property": "fontFamily", "selectors": [["@font-face"]], "value": { "src": "url('/fonts/font.woff2') format ('woff2'), url('/fonts/font.woff') format ('woff')" } })
const expect = null
t.deepEqual (actual, expect) })
+end_src
+HTML:
**** getPlaceholders
+HTML:
+begin_src js :tangle src/build/get-placeholders.js
import { parseIdentifier } from "../api/index.js"
export function getPlaceholders (selectors = []) { return selectors.map (function (selector) { return (/^%/u).test (selector) ? ".".concat ( parseIdentifier ({ "property": selector, "value": selector }).identifier ) : selector }) }
+end_src
+HTML:
+HTML:
+begin_src js :tangle src/build/get-placeholders.test.js
import ava from "ava" import { getPlaceholders } from "./get-placeholders.js"
ava ("given undefined arguments", (t) => { const actual = getPlaceholders ()
const expect = []
t.deepEqual (actual, expect) })
ava ("given an array of selectors with placeholders", (t) => { const actual = getPlaceholders (["a", "%b", ".c", "%products", "%items"])
const expect = ["a", ".afqkz", ".c", ".afknd", ".afxpz"]
t.deepEqual (actual, expect) })
+end_src
+HTML:
**** getPropertyId
+HTML:
+begin_src js :tangle src/build/get-property-id.js
import { camelCase } from "../api/index.js"
/**
- @param {string} propertyName
- Property name/identifier specifying a stylistic CSS feature to change.
- @returns {number} */
export function getPropertyId (propertyName = "") { const n = parseInt ("af", 36)
switch (true) { case (/^%/u).test (propertyName): return 0 + n case (/^\x2D\x2D/u).test (propertyName): return 1 + n default: return ( "$,--,all,direction,unicodeBidi,writingMode,textOrientation,glyphOrientationVertical,textCombineUpright,textTransform,whiteSpace,textSpaceCollapse,textSpaceTrim,tabSize,wordBreak,lineBreak,hyphens,overflowWrap,wordWrap,textWrap,wrapBefore,wrapAfter,wrapInside,hyphenateCharacter,hyphenateLimitZone,hyphenateLimitChars,hyphenateLimitLines,hyphenateLimitLast,textAlign,textAlignAll,textAlignLast,textJustify,textGroupAlign,wordSpacing,letterSpacing,linePadding,textSpacing,textIndent,hangingPunctuation,textDecoration,textDecorationLine,textDecorationStyle,textDecorationColor,textDecorationWidth,textDecorationSkip,textDecorationSkipInk,textUnderlineOffset,textUnderlinePosition,textEmphasis,textEmphasisStyle,textEmphasisColor,textEmphasisPosition,textEmphasisSkip,textShadow,src,font,fontStyle,fontVariant,fontWeight,fontStretch,fontSize,lineHeight,fontFamily,fontMinSize,fontMaxSize,fontSizeAdjust,fontSynthesis,fontSynthesisWeight,fontSynthesisStyle,fontSynthesisSmallCaps,unicodeRange,fontFeatureSettings,fontVariationSettings,fontLanguageOverride,fontKerning,fontVariantLigatures,fontVariantPosition,fontVariantCaps,fontVariantNumeric,fontVariantAlternates,fontVariantEastAsian,fontOpticalSizing,fontPalette,fontVariantEmoji,content,quotes,stringSet,bookmarkLevel,bookmarkLabel,bookmarkState,running,footnoteDisplay,footnotePolicy,outline,outlineColor,outlineStyle,outlineWidth,outlineOffset,resize,textOverflow,cursor,caret,caretColor,caretShape,navUp,navRight,navDown,navLeft,userSelect,appearance,position,top,right,bottom,left,offsetBefore,offsetAfter,offsetStart,offsetEnd,zIndex,display,contain,width,height,minWidth,minHeight,maxWidth,maxHeight,boxSizing,visibility,pageBreakBefore,pageBreakAfter,pageBreakInside,margin,marginTop,marginRight,marginBottom,marginLeft,marginTrim,padding,paddingTop,paddingRight,paddingBottom,paddingLeft,dominantBaseline,verticalAlign,alignmentBaseline,baselineShift,inlineSizing,initialLetters,initialLettersAlign,initialLettersWrap,listStyle,listStyleType,listStylePosition,listStyleImage,markerSide,counterReset,counterSet,counterIncrement,overflow,overflowX,overflowY,overflowBlock,overflowInline,blockOverflow,lineClamp,maxLines,continue,tableLayout,borderCollapse,borderSpacing,captionSide,emptyCells,flexFlow,flexDirection,flexWrap,order,flex,flexGrow,flexShrink,flexBasis,placeContent,alignContent,justifyContent,placeItems,alignItems,justifyItems,placeSelf,alignSelf,justifySelf,gap,rowGap,columnGap,columns,columnWidth,columnCount,columnRule,columnRuleWidth,columnRuleStyle,columnRuleColor,columnSpan,columnFill,flowInto,flowFrom,regionFragment,breakBefore,breakAfter,breakInside,orphans,widows,boxDecorationBreak,grid,gridTemplate,gridTemplateRows,gridTemplateColumns,gridTemplateAreas,gridAutoFlow,gridAutoRows,gridAutoColumns,gridArea,gridRow,gridRowStart,gridRowEnd,gridColumn,gridColumnStart,gridColumnEnd,rubyPosition,rubyMerge,rubyAlign,float,clear,blockSize,inlineSize,minBlockSize,minInlineSize,maxBlockSize,maxInlineSize,marginBlock,marginBlockStart,marginBlockEnd,marginInline,marginInlineStart,marginInlineEnd,inset,insetBlock,insetBlockStart,insetBlockEnd,insetInline,insetInlineStart,insetInlineEnd,paddingBlock,paddingBlockStart,paddingBlockEnd,paddingInline,paddingInlineStart,paddingInlineEnd,borderBlockWidth,borderBlockStartWidth,borderBlockEndWidth,borderInlineWidth,borderInlineStartWidth,borderInlineEndWidth,borderBlockStyle,borderBlockStartStyle,borderBlockEndStyle,borderInlineStyle,borderInlineStartStyle,borderInlineEndStyle,borderBlockColor,borderBlockStartColor,borderBlockEndColor,borderInlineColor,borderInlineStartColor,borderInlineEndColor,borderBlock,borderBlockStart,borderBlockEnd,borderInline,borderInlineStart,borderInlineEnd,borderStartStartRadius,borderStartEndRadius,borderEndStartRadius,borderEndEndRadius,fillRule,fillBreak,fill,fillColor,fillImage,fillOrigin,fillPosition,fillSize,fillRepeat,fillOpacity,strokeWidth,strokeAlign,strokeLinecap,strokeLinejoin,strokeMiterlimit,strokeBreak,strokeDasharray,strokeDashoffset,strokeDashCorner,strokeDashJustify,stroke,strokeColor,strokeImage,strokeOrigin,strokePosition,strokeSize,strokeRepeat,strokeOpacity,marker,markerStart,markerMid,markerEnd,markerSegment,markerPattern,markerKnockoutLeft,markerKnockoutRight,vectorEffect,colorRendering,shapeRendering,textRendering,imageRendering,bufferedRendering,stopColor,stopOpacity,color,opacity,colorAdjust,objectFit,objectPosition,imageResolution,imageOrientation,imageRendering,background,backgroundColor,backgroundImage,backgroundPosition,backgroundPositionX,backgroundPositionY,backgroundSize,backgroundRepeat,backgroundAttachment,backgroundOrigin,backgroundClip,border,borderTop,borderRight,borderBottom,borderLeft,borderWidth,borderTopWidth,borderRightWidth,borderBottomWidth,borderLeftWidth,borderStyle,borderTopStyle,borderRightStyle,borderBottomStyle,borderLeftStyle,borderColor,borderTopColor,borderRightColor,borderBottomColor,borderLeftColor,borderRadius,borderTopLeftRadius,borderTopRightRadius,borderBottomRightRadius,borderBottomLeftRadius,borderImage,borderImageSource,borderImageSlice,borderImageWidth,borderImageOutset,borderImageRepeat,boxShadow,clip,clipPath,clipRule,mask,maskImage,maskPosition,maskSize,maskRepeat,maskOrigin,maskClip,maskComposite,maskMode,maskBorder,maskBorderSource,maskBorderSlice,maskBorderWidth,maskBorderOutset,maskBorderRepeat,maskBorderMode,maskType,shapeOutside,shapeImageThreshold,shapeMargin,filter,floodColor,floodOpacity,colorInterpolationFilters,lightingColor,mixBlendMode,isolation,backgroundBlendMode,transition,transitionProperty,transitionDuration,transitionTimingFunction,transitionDelay,transform,transformOrigin,transformBox,transformStyle,perspective,perspectiveOrigin,backfaceVisibility,animation,animationName,animationDuration,animationTimingFunction,animationDelay,animationIterationCount,animationDirection,animationFillMode,animationPlayState,offset,offsetPosition,offsetPath,offsetDistance,offsetRotate,offsetAnchor,willChange,scrollSnapType,scrollPadding,scrollPaddingTop,scrollPaddingRight,scrollPaddingBottom,scrollPaddingLeft,scrollPaddingBlock,scrollPaddingBlockStart,scrollPaddingBlockEnd,scrollPaddingInline,scrollPaddingInlineStart,scrollPaddingInlineEnd,scrollMargin,scrollMarginTop,scrollMarginRight,scrollMarginBottom,scrollMarginLeft,scrollMarginBlock,scrollMarginBlockStart,scrollMarginBlockEnd,scrollMarginInline,scrollMarginInlineStart,scrollMarginInlineEnd,scrollSnapAlign,scrollSnapStop,scrollBehavior" .split (",") .indexOf ( camelCase (propertyName) .replace (/^(Moz|Ms|Webkit)/u, "") .replace (/^([A-Z])/u, function (a) { return a.toLowerCase () }) ) + n ) } }
+end_src
+HTML:
+HTML:
+begin_src json :tangle src/build/get-property-id.json
{ "variables": [ "%", "--" ],
"cascade": [ "all" ],
"writingModes": [ "direction", "unicodeBidi", "writingMode", "textOrientation", "glyphOrientationVertical", "textCombineUpright" ],
"text": [ "textTransform",
"whiteSpace",
"textSpaceCollapse",
"textSpaceTrim",
"tabSize",
"wordBreak",
"lineBreak",
"hyphens",
"overflowWrap",
"wordWrap",
"textWrap",
"wrapBefore",
"wrapAfter",
"wrapInside",
"hyphenateCharacter",
"hyphenateLimitZone",
"hyphenateLimitChars",
"hyphenateLimitLines",
"hyphenateLimitLast",
"textAlign",
"textAlignAll",
"textAlignLast",
"textJustify",
"textGroupAlign",
"wordSpacing",
"letterSpacing",
"linePadding",
"textSpacing",
"textIndent",
"hangingPunctuation"
],
"textDecor": [ "textDecoration", "textDecorationLine", "textDecorationStyle", "textDecorationColor",
"textDecorationWidth",
"textDecorationSkip",
"textDecorationSkipInk",
"textUnderlineOffset",
"textUnderlinePosition",
"textEmphasis",
"textEmphasisStyle",
"textEmphasisColor",
"textEmphasisPosition",
"textEmphasisSkip",
"textShadow"
],
"fonts": [ "src",
"font",
"fontStyle",
"fontVariant",
"fontWeight",
"fontStretch",
"fontSize",
"lineHeight",
"fontFamily",
"fontMinSize",
"fontMaxSize",
"fontSizeAdjust",
"fontSynthesis",
"fontSynthesisWeight",
"fontSynthesisStyle",
"fontSynthesisSmallCaps",
"unicodeRange",
"fontFeatureSettings",
"fontVariationSettings",
"fontLanguageOverride",
"fontKerning",
"fontVariantLigatures",
"fontVariantPosition",
"fontVariantCaps",
"fontVariantNumeric",
"fontVariantAlternates",
"fontVariantEastAsian",
"fontOpticalSizing",
"fontPalette",
"fontVariantEmoji"
],
"content": [ "content", "quotes", "stringSet", "bookmarkLevel", "bookmarkLabel", "bookmarkState" ],
"gcpm": [ "running", "footnoteDisplay", "footnotePolicy" ],
"ui": [ "outline", "outlineColor", "outlineStyle", "outlineWidth",
"outlineOffset",
"resize",
"textOverflow",
"cursor",
"caret",
"caretColor",
"caretShape",
"navUp",
"navRight",
"navDown",
"navLeft",
"userSelect",
"appearance"
],
"position": [ "position",
"top",
"right",
"bottom",
"left",
"offsetBefore",
"offsetAfter",
"offsetStart",
"offsetEnd",
"zIndex"
],
"display": [ "display" ],
"contain": [ "contain" ],
"sizing": [ "width", "height",
"minWidth",
"minHeight",
"maxWidth",
"maxHeight",
"boxSizing"
],
"css2": [ "visibility",
"pageBreakBefore",
"pageBreakAfter",
"pageBreakInside"
],
"box": [ "margin", "marginTop", "marginRight", "marginBottom", "marginLeft",
"marginTrim",
"padding",
"paddingTop",
"paddingRight",
"paddingBottom",
"paddingLeft"
],
"inline": [ "dominantBaseline", "verticalAlign", "alignmentBaseline", "baselineShift",
"inlineSizing",
"initialLetters",
"initialLettersAlign",
"initialLettersWrap"
],
"lists": [ "listStyle", "listStyleType", "listStylePosition", "listStyleImage",
"markerSide",
"counterReset",
"counterSet",
"counterIncrement"
],
"overflow": [ "overflow", "overflowX", "overflowY",
"overflowBlock",
"overflowInline",
"blockOverflow",
"lineClamp",
"maxLines",
"continue"
],
"tables": [ "tableLayout", "borderCollapse", "borderSpacing", "captionSide",
"emptyCells"
],
"flexbox": [ "flexFlow", "flexDirection", "flexWrap",
"order",
"flex",
"flexGrow",
"flexShrink",
"flexBasis"
],
"align": [ "placeContent", "alignContent", "justifyContent",
"placeItems",
"alignItems",
"justifyItems",
"placeSelf",
"alignSelf",
"justifySelf",
"gap",
"rowGap",
"columnGap"
],
"multicol": [ "columns", "columnWidth", "columnCount",
"columnRule",
"columnRuleWidth",
"columnRuleStyle",
"columnRuleColor",
"columnSpan",
"columnFill"
],
"regions": [ "flowInto", "flowFrom", "regionFragment" ],
"break": [ "breakBefore", "breakAfter", "breakInside", "orphans", "widows", "boxDecorationBreak" ],
"grid": [ "grid",
"gridTemplate",
"gridTemplateRows",
"gridTemplateColumns",
"gridTemplateAreas",
"gridAutoFlow",
"gridAutoRows",
"gridAutoColumns",
"gridArea",
"gridRow",
"gridRowStart",
"gridRowEnd",
"gridColumn",
"gridColumnStart",
"gridColumnEnd"
],
"ruby": [ "rubyPosition", "rubyMerge", "rubyAlign" ],
"logical": [ "float", "clear",
"blockSize",
"inlineSize",
"minBlockSize",
"minInlineSize",
"maxBlockSize",
"maxInlineSize",
"marginBlock",
"marginBlockStart",
"marginBlockEnd",
"marginInline",
"marginInlineStart",
"marginInlineEnd",
"inset",
"insetBlock",
"insetBlockStart",
"insetBlockEnd",
"insetInline",
"insetInlineStart",
"insetInlineEnd",
"paddingBlock",
"paddingBlockStart",
"paddingBlockEnd",
"paddingInline",
"paddingInlineStart",
"paddingInlineEnd",
"borderBlockWidth",
"borderBlockStartWidth",
"borderBlockEndWidth",
"borderInlineWidth",
"borderInlineStartWidth",
"borderInlineEndWidth",
"borderBlockStyle",
"borderBlockStartStyle",
"borderBlockEndStyle",
"borderInlineStyle",
"borderInlineStartStyle",
"borderInlineEndStyle",
"borderBlockColor",
"borderBlockStartColor",
"borderBlockEndColor",
"borderInlineColor",
"borderInlineStartColor",
"borderInlineEndColor",
"borderBlock",
"borderBlockStart",
"borderBlockEnd",
"borderInline",
"borderInlineStart",
"borderInlineEnd",
"borderStartStartRadius",
"borderStartEndRadius",
"borderEndStartRadius",
"borderEndEndRadius"
],
"fillStroke": [ "fillRule", "fillBreak",
"fill",
"fillColor",
"fillImage",
"fillOrigin",
"fillPosition",
"fillSize",
"fillRepeat",
"fillOpacity",
"strokeWidth",
"strokeAlign",
"strokeLinecap",
"strokeLinejoin",
"strokeMiterlimit",
"strokeBreak",
"strokeDasharray",
"strokeDashoffset",
"strokeDashCorner",
"strokeDashJustify",
"stroke",
"strokeColor",
"strokeImage",
"strokeOrigin",
"strokePosition",
"strokeSize",
"strokeRepeat",
"strokeOpacity"
],
"svgMarkers": [ "marker", "markerStart", "markerMid", "markerEnd",
"markerSegment",
"markerPattern",
"markerKnockoutLeft",
"markerKnockoutRight"
],
"svgTiny": [ "vectorEffect",
"colorRendering",
"shapeRendering",
"textRendering",
"imageRendering",
"bufferedRendering",
"stopColor",
"stopOpacity"
],
"color": [ "color", "opacity",
"colorAdjust"
],
"images": [ "objectFit", "objectPosition",
"imageResolution",
"imageOrientation",
"imageRendering"
],
"backgrounds": [ "background", "backgroundColor", "backgroundImage", "backgroundPosition", "backgroundPositionX", "backgroundPositionY", "backgroundSize", "backgroundRepeat", "backgroundAttachment", "backgroundOrigin", "backgroundClip",
"border",
"borderTop",
"borderRight",
"borderBottom",
"borderLeft",
"borderWidth",
"borderTopWidth",
"borderRightWidth",
"borderBottomWidth",
"borderLeftWidth",
"borderStyle",
"borderTopStyle",
"borderRightStyle",
"borderBottomStyle",
"borderLeftStyle",
"borderColor",
"borderTopColor",
"borderRightColor",
"borderBottomColor",
"borderLeftColor",
"borderRadius",
"borderTopLeftRadius",
"borderTopRightRadius",
"borderBottomRightRadius",
"borderBottomLeftRadius",
"borderImage",
"borderImageSource",
"borderImageSlice",
"borderImageWidth",
"borderImageOutset",
"borderImageRepeat",
"boxShadow"
],
"masking": [ "clip", "clipPath", "clipRule",
"mask",
"maskImage",
"maskPosition",
"maskSize",
"maskRepeat",
"maskOrigin",
"maskClip",
"maskComposite",
"maskMode",
"maskBorder",
"maskBorderSource",
"maskBorderSlice",
"maskBorderWidth",
"maskBorderOutset",
"maskBorderRepeat",
"maskBorderMode",
"maskType"
],
"shapes": [ "shapeOutside", "shapeImageThreshold", "shapeMargin" ],
"filterEffects": [ "filter", "floodColor", "floodOpacity", "colorInterpolationFilters", "lightingColor" ],
"compositing": [ "mixBlendMode", "isolation", "backgroundBlendMode" ],
"transitions": [ "transition", "transitionProperty", "transitionDuration", "transitionTimingFunction", "transitionDelay" ],
"transforms": [ "transform", "transformOrigin", "transformBox", "transformStyle",
"perspective",
"perspectiveOrigin",
"backfaceVisibility"
],
"animations": [ "animation", "animationName", "animationDuration", "animationTimingFunction", "animationDelay", "animationIterationCount", "animationDirection", "animationFillMode", "animationPlayState" ],
"motion": [ "offset", "offsetPosition", "offsetPath", "offsetDistance", "offsetRotate", "offsetAnchor" ],
"willChange": [ "willChange" ],
"scrollSnap": [ "scrollSnapType",
"scrollPadding",
"scrollPaddingTop",
"scrollPaddingRight",
"scrollPaddingBottom",
"scrollPaddingLeft",
"scrollPaddingBlock",
"scrollPaddingBlockStart",
"scrollPaddingBlockEnd",
"scrollPaddingInline",
"scrollPaddingInlineStart",
"scrollPaddingInlineEnd",
"scrollMargin",
"scrollMarginTop",
"scrollMarginRight",
"scrollMarginBottom",
"scrollMarginLeft",
"scrollMarginBlock",
"scrollMarginBlockStart",
"scrollMarginBlockEnd",
"scrollMarginInline",
"scrollMarginInlineStart",
"scrollMarginInlineEnd",
"scrollSnapAlign",
"scrollSnapStop"
],
"cssomView": [ "scrollBehavior" ] }
+end_src
+HTML:
+HTML:
+begin_src js :tangle src/build/get-property-id.test.js
import ava from "ava" import { getPropertyId } from "./get-property-id.js"
ava ("given undefined arguments", (t) => { const actual = getPropertyId () const expect = 374
t.is (actual, expect) })
ava ("given an empty string", (t) => { const actual = getPropertyId ("") const expect = 374
t.is (actual, expect) })
ava ("given a string with an invalid property name", (t) => { const actual = getPropertyId ("xyz") const expect = 374
t.is (actual, expect) })
ava ("given a string with a valid placeholder class name", (t) => { const actual = getPropertyId ("%productList") const expect = 375
t.is (actual, expect) })
ava ("given a string with a valid CSS variable name", (t) => { const actual = getPropertyId ("--background-color") const expect = 376
t.is (actual, expect) })
ava ("given a string with a valid property name (1)", (t) => { const actual = getPropertyId ("background") const expect = 712
t.is (actual, expect) })
ava ("given a string with a valid property name (2)", (t) => { const actual = getPropertyId ("background-color") const expect = 713
t.is (actual, expect) })
ava ("given a string with a prefixed property name (1)", (t) => { const actual = getPropertyId ("-webkit-appearance") const expect = 484
t.is (actual, expect) })
ava ("given a string with a prefixed property name (2)", (t) => { const actual = getPropertyId ("MozUserSelect") const expect = 483
t.is (actual, expect) })
+end_src
+HTML:
**** getSelectors
+HTML:
+begin_src js :tangle src/build/get-selectors.js
/**
- @param {string} selectors
- String identifying the elements to which a set of CSS rulesets apply.
- @returns {?RegExpMatchArray} */
export function getSelectors (selectors = "") { const identifier = "-?[A-Z_a-z\u{00a0}-\u{ffff}]+[-0-9A-Z_a-z\u{00a0}-\u{ffff}]*"
const regex = new RegExp ( [ "(&)",
"(#".concat (identifier, ")"),
"(\\.".concat (identifier, ")"),
"(\\