fix color-schemes

This commit is contained in:
Talon Poole 2021-02-20 21:22:37 +00:00
parent 17a2ab7f5e
commit 94abcfd542
5 changed files with 80 additions and 48 deletions

View file

@ -162,15 +162,13 @@ gmi-web --config web.json $(find ~/gmi/dst -name '*.gmi')
## custom css
It is possible to use your own set of completely custom CSS rules by pointing to a .css file containing them.
It is possible to use your own set of completely custom CSS rules _and_ variables by pointing to a .css file containing them.
```sh
gmi-web --body --css custom.css < doc.gmi
```
gmi-web auto-detects the need for the `<meta>` color-schemes and allows for using `--inline` to insert the declarations as `style` properties on their respective blocks.
[gmi.css](./gmi.css) is a good starting point!
gmi-web auto-detects the need for the `<meta>` color-schemes and allows for using `--inline` to insert the declarations as `style` properties on their respective blocks. Variables defined in `:root` will be picked up and made available for customization via command-line flags.
# license

3
cli.js
View file

@ -104,6 +104,9 @@ if (!argv.html && !argv.body) {
cli.exit(1);
}
argv.inlineCSS = CSS.inline(argv);
argv.styleTag = CSS.style(argv);
if (!argv.files) {
let gemtext;
try {

95
css.js
View file

@ -6,10 +6,10 @@ import { stringify, parse } from "css";
export function style(options) {
if (options.inline || options.css === "none") return "";
const rules = load(options);
const schemes = rules.find(({ media }) => media === "preferes-color-scheme")
? `<meta name="color-scheme" content="dark light">\n`
const schemes = rules.find(({ media }) => media)
? `<meta name="color-scheme" content="dark light">`
: "";
return `<style>${stringify(
return `\n${schemes}\n<style>${stringify(
{
stylesheet: { rules: reduceVariables(rules, options) },
},
@ -19,7 +19,7 @@ export function style(options) {
export const inline = (options) => {
const rules = load(options);
return (tag) => {
return function inlineCSS(tag) {
if (!options.inline || options.css === "none") return "";
const styles = reduceVariables(rules, options)
.filter(({ selectors }) => selectors && selectors.includes(tag))
@ -37,25 +37,22 @@ export function load(options) {
options.css =
options.css ||
(!options.body || !options.inline ? "gmi-web.css" : "gmi.css");
console.log("load:", options.css);
if (
["gmi", "web", "gmi.css", "gmi-web.css"].includes(options.css) ||
["gmi.css", "gmi-web.css"].includes(options.css) ||
existsSync(options.css)
) {
const packageRoot = (file) =>
const internal = (file) =>
path.resolve(path.dirname(new URL(import.meta.url).pathname), file);
return parse(
readFileSync(
path.resolve(
["gmi-web.css", "web"].includes(options.css)
? packageRoot("gmi-web.css")
: ["gmi.css", "gmi"].includes(options.css)
? packageRoot("gmi.css")
: resolve(options.css)
),
"utf-8"
)
).stylesheet.rules
readFileSync(
path.resolve(
["gmi-web.css", "gmi.css"].includes(options.css)
? internal(options.css)
: resolve(options.css)
),
"utf-8"
)
).stylesheet.rules;
} else {
throw new Error(`Cannot find file ${options.css}.`);
}
@ -77,21 +74,55 @@ function reduceVariables(rules, options) {
const defaultVariables = rootVariables(rules);
const CSS_VAR = /(^var\((?<key>.+)\)|(?<val>.+))/;
return rules
.filter(({ selectors }) => selectors && !selectors.includes(":root"))
.map((rule) =>
Object.assign(
rule,
rule.declarations
? {
declarations: rule.declarations.map((declaration) => {
let { key, val } = CSS_VAR.exec(declaration.value).groups;
// only one level of variable referencing is supported
key =
CSS_VAR.exec(options[key] || defaultVariables[key]).groups
.key || key;
return Object.assign(declaration, {
value: key
? options[key] || defaultVariables[key]
: declaration.value,
});
}),
}
: {}
)
)
.map((rule) => {
if (!rule.media) return rule;
return Object.assign(rule, {
declarations: rule.declarations.map((declaration) => {
let { key, val } = CSS_VAR.exec(declaration.value).groups;
// only one level of variable referencing is supported
key =
CSS_VAR.exec(options[key] || defaultVariables[key]).groups.key ||
key;
return Object.assign(declaration, {
value: key
? options[key] || defaultVariables[key]
: declaration.value,
});
}),
rules: rule.rules.map((rule) =>
Object.assign(
rule,
rule.declarations
? {
declarations: rule.declarations.map((declaration) => {
let { key, val } = CSS_VAR.exec(declaration.value).groups;
// only one level of variable referencing is supported
key =
CSS_VAR.exec(options[key] || defaultVariables[key]).groups
.key || key;
return Object.assign(declaration, {
value: key
? options[key] || defaultVariables[key]
: declaration.value,
});
}),
}
: {}
)
),
});
});
})
.filter(
({ selectors, media }) =>
media || (selectors && !selectors.includes(":root"))
);
}

View file

@ -30,20 +30,21 @@ fashion!
file as the description <meta> tag. _LIMIT_ may be used to truncate the text
with an ellipsis at that number of characters.
*--css* _MODE_ | _FILE_
*--html* will default _MODE_ to *web* enabling a handful of font and color
variables. See *--help* for the complete list.
*--css* _FILE_
*--body* will default to the internal *gmi.css* and use just what is needed
to fix CSS Normal Flow quirks.
*--body* will default _MODE_ to *gmi* and use just what is needed to fix
CSS Normal Flow quirks.
*--html* will default to the internal *gmi-web.css* enabling a handful of font
and color variables. See *--help* for the complete list.
Pointing to a CSS _FILE_ will use those styles. This works with *--body* or
*--inline* and auto-detects the need for the <meta> color-schemes.
Pointing to a CSS _FILE_ will use those styles _and_ variables. This works
with *--body* or *--inline* and auto-detects the need for the <meta>
color-schemes.
*--inline* will insert the declarations as "style" properties on their
respective blocks. This is the only behavior when using *--body*.
Choosing _MODE_ *none* will not include any style information.
Using *none* will not include any style information.
*--image | --audio | --video* _EXTENSIONS_
Include media extensions inline. You can provide multiple extensions per flag

View file

@ -95,7 +95,6 @@ export function body(tokens, options) {
export function toHTML(gemtext, options) {
const tokens = tokenize(gemtext);
options.inlineCSS = CSS.inline(options);
if (options.body) return body(tokens, options);
@ -119,9 +118,9 @@ ${body(tokens, options)}
export function head(options) {
return `
<meta charset="${options.charset}">
<meta name="viewport" content="width=device-width,initial-scale=1">${CSS.style(
options
)}
<meta name="viewport" content="width=device-width,initial-scale=1">${
options.styleTag
}
<title>${options.title}</title>${
!options.author ? "" : `\n<meta name="author" content="${options.author}">`
}${