Public
Edited
Jan 20, 2023
Importers
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
registerStyle(Style)
Insert cell
Insert cell
Tokens = ({
ButtonBG: Color,
Text: { color: "#30300", size: "14px", font: "sans-serif" },
pad: (_) => unit(_ * 4, "px"),
bd: (_) => unit(_ * 2, "px")
})
Insert cell
Style = style(
{
"&": {
background: "${ButtonBG}A0",
cursor: "help",
display: "inline-block",
fontSize: "${Text.size}",
color: "${Text.color}",
fontFamily: "${Text.font}",
borderRadius: "$2bd",
padding: "$2pad $3pad"
},
"& code": {
opacity: 0.5,
fontSize: "${Text.size}"
},
"&:hover": {
background: "${ButtonBG}"
}
},
Tokens
)
Insert cell
Insert cell
Insert cell
style = (rules, context = undefined, name = undefined, parser = parseToken) => {
rules = expandRules(rules, context, parser);
const className = `.${name ? name : alpha(hash(rules))}`;
return reduce(
rules,
(r, v, k) => {
r[k.replace("&", className)] = v;
},
{}
);
}
Insert cell
className = (...rules) =>
[
...rules
.reduce((r, v) => {
for (let k in v) {
const match = k.match(RE_RULEHASH);
if (match) {
const n = match.groups.hash;
r.set(n, true);
}
}
return r;
}, new Map())
.keys()
].join(" ")
Insert cell
Insert cell
registerStyle = (style, name = "default", reset = true) => {
return html`<style>${(style instanceof Array ? style : [style])
.map((_) => compileStyle(_).join("\n"))
.join("\n")}`;
}
Insert cell
Insert cell
Insert cell
compileStyle(
style({
"@": [
"import url('https://fonts.googleapis.com/css2?family=Karla:wght@400;500;600;700;800&display=swap')"
],
"&": {
fontSize: "1rem",
lineHeight: "1.25em",
fontFamily: "$text",
fontWeight: 200,
color: "$text"
}
})
)
Insert cell
compileStatement = (name, value) =>
name === "content"
? `content: ${JSON.stringify(value)};`
: `${name}: ${value};`
Insert cell
compileStyle = (style) =>
reduce(
style,
(r, properties, scope) => {
const p =
properties instanceof Array
? properties
: reduce(
properties,
(r, value, name) => {
r.push(compileStatement(propertyName(name), value));
},
[]
);

if (scope === "@") {
return r.concat(p.map((_) => `@${_};`));
} else {
r.push(`${scope} {${p.join("")}}`);
return r;
}
},
[]
)
Insert cell
Insert cell
Insert cell
expandRules = {
const expandRules = (rules, context = undefined, parser = parseToken) =>
map(rules, (v, k) =>
v instanceof Object
? expandRules(v, context, parser)
: expandProperty(v, k, context, parser)
);
return expandRules;
}
Insert cell
Insert cell
expandProperty = (
value,
name = undefined,
context = undefined,
parser = parseToken
) => {
const res = [];
let match = null;
let offset = 0;
while ((match = RE_TEMPLATE.exec(value)) !== null) {
// We push the inbetween text
res.push(value.substring(offset, match.index));
// We parse the token and push the result
res.push(
parser(
match.groups.token || match.groups.expr,
context,
match.groups.expr ? true : false
)
);
offset = match.index + match[0].length;
}
if (offset === 0) {
return value;
} else {
res.push(value.substring(offset, value.length));
return res.join("");
}
return value;
}
Insert cell
parseToken = (token, context) => {
const match = RE_TOKEN_CHAIN.exec(token);
// We reduce the tokens
return match
? match[0].split(".").reduce((r, v, i) => {
const m = RE_TOKEN.exec(v);
if (m.groups) {
const { number, decimal, unit, token } = m.groups;
const w = number
? evalNumber(
decimal ? parseFloat(number) : parseInt(number),
unit,
r
)
: evalToken(token, r);
return w;
} else {
return r;
}
return r;
}, context)
: token;
}
Insert cell
expandProperty("${text.font}", undefined, {
text: { font: "sans-serif" }
})
Insert cell
expandProperty("$2pad $4pad", undefined, {
pad: (_) => unit(_ * 8, "px")
})
Insert cell
evalNumber = (number, unit, context) =>
context && context[unit] ? context[unit](number) : `${number}${unit || "px"}`
Insert cell
evalToken = (token, context) =>
context && context[token] ? context[token] : `var(--${token})`
Insert cell
Insert cell
RE_TEMPLATE = new RegExp(
"\\$((?<token>[\\w_]+(\\.[\\w_]+)*)|{(?<expr>[^}]+)})",
"g"
)
Insert cell
map({ number: "$10pad", tokenA: "${RED_LT}A10", tokenB: "${text.size}" }, (_) =>
_.match(RE_TEMPLATE)
)
Insert cell
RE_TOKEN_CHAIN = {
const expr = "((\\d+(\\.\\d+)?)(\\w+)|([\\w\\d_]+))";
const chain = `${expr}(\\.${expr})*`;
return new RegExp(chain, "g");
}
Insert cell
RE_TOKEN = new RegExp(
"(?<number>\\d+(?<decimal>\\.\\d+)?)(?<unit>\\w+)|(?<token>[\\w\\d_]+)"
)
Insert cell
RE_RULEHASH = new RegExp("^\\.(?<hash>[A-Za-z0-9]+)")
Insert cell
map({ number: "10pad", tokenA: "RED_LT_A10", tokenB: "REDA0" }, (_) =>
RE_TOKEN.exec(_)
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
numfmt = (value, precision = 0) => {
const p = Math.pow(10, precision);
const v = parseFloat(value);
const w = Math.round(v * p);
const k = `${w}`;
const i = k.length - precision;
return precision && v * p != w
? `${k.substring(0, i) || "0"}.${k.substring(i) || "0"}`
: value;
}
Insert cell
Insert cell
unit(100, "vh")
Insert cell
propertyName = (name) => {
if (name && name.startsWith("--")) return name;
else {
const property = RE_CSS_PROPERTY;
const res = [];
let match = null;
while ((match = property.exec(name)) !== null) {
res.push(match[0].toLowerCase());
}
return res.join("-");
}
};
Insert cell
propertyName("borderRadius")
Insert cell
Insert cell
Insert cell
map({ a: 1, b: 2, c: 3 }, (_) => _ * 10)
Insert cell
Insert cell
reduce({ a: 10, b: 20, c: 30 }, (r, v) => r + v, 0)
Insert cell
Insert cell
hash("One two three four")
Insert cell
Insert cell
alpha(hash("One two three four"))
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more