Proposal: new and better `UserStyle` metadata block
I know I've raised:
- #711
But I could still see the value of writing CSS code directly instead of wrapping it in .user.js:
- Better syntax highlighting
- CSS linting
- Simpler and more straightforward
- Directly utilize the relevant extension APIs
From my knowledge, some of the issues currently faced by UserStyle ecology:
- Lack of true standardization and widespread adoption
- The
@match *://*/*conflict in metadata comment - Deprecated
@-moz-documentsyntax
I hope it does:
- Based on
.cssfiles instead of individual apps internal format - Use extension API registration only once for each single file
- No complex splitting and parsing and acts like a normal css file (or userjs)
- Accepted by most common editors with no errors
- Proper syntax highlighting and markdown rendering (Like the following example)
- The
UserStylemetadata easy to write and parse within the file - Prefer
match patternsover other matching rules (Safer and API friendly) - Not confusing and conflicting with various existing implementations
Evolution:
- Using CSS comments becomes impractical due to the
@match *://*/*conflict. - Using the
@metadatalikedAt-rulesis prone to linting errors - So a fairly straightforward idea would be to utilize the existing CSS syntax
- It is quite compatible and meets all of the above considerations
- This metadata ruleset will be parsed and stripped by the manager
- Use
Custom propertiesfor metadata names to avoid linting errors
The following is a simple sample, for further improvement and standardization:
Existing one in the userscripts:
/* ==UserStyle==
@name NewStyle-aefwe6zy
@description This is your new file, start writing code
@match <all_urls>
@match *://*/*
@match *://*.foo.bar/*
==/UserStyle== */
body { color: red; }
New proposed:
userstyle-metadata {
--name: "NewStyle-aefwe6zy";
--description: "This is your new file, start writing code";
--match: "<all_urls>";
--match-2: "*://*/*";
--match-3: "*://*.foo.bar/*";
--version: "1.0.0";
--download-url: "https://foo.bar/foo.user.css";
}
body { color: red; }
Footnotes: To avoid lint error like Unexpected duplicate "--match" (declaration-block-no-duplicate-custom-properties), metadata such as @match which can be declared repeatedly, can have any trailing suffix and will eventually be parsed as an array by the manager.
For example, this is a simple parser implementation in the browser runtime:
const cssText = `
userstyle-metadata {
--name: "NewStyle-aefwe6zy";
--description: "This is your new file, start writing code";
--match: "<all_urls>";
--match-2: "*://*/*";
--match-3: "*://*.foo.bar/*";
--version: "1.0.0";
--download-url: "https://foo.bar/foo{}}.user.css";
}
body { color: red; }
h1 { color: red; }`;
async function parseUserStyleText(cssText) {
const stylesheet = new CSSStyleSheet();
await stylesheet.replace(cssText);
const cssTexts = [];
let metadataRule;
for (const rule of stylesheet.cssRules) {
if (rule.selectorText === "userstyle-metadata") {
metadataRule = rule;
continue;
}
cssTexts.push(rule.cssText);
}
if (!metadataRule) throw Error("metadata not found");
const mKeys = ["name", "description", "version", "download-url"];
const metadata = {
match: [],
};
for (const key of metadataRule.style) {
const value = metadataRule.style
.getPropertyValue(key)
.replace(/^["']+|["']+$/g, "");
if (key.startsWith("--match")) {
metadata.match.push(value);
}
const mKey = key.slice(2);
if (mKeys.includes(mKey)) {
metadata[mKey] = value;
}
}
return { metadata, css: cssTexts.join("\n") };
}
const result = await parseUserStyleText(cssText);
console.log(result);
{
"metadata": {
"match": [
"<all_urls>",
"*://*/*",
"*://*.foo.bar/*"
],
"name": "NewStyle-aefwe6zy",
"description": "This is your new file, start writing code",
"version": "1.0.0",
"download-url": "https://foo.bar/foo{}}.user.css"
},
"css": "body { color: red; }\nh1 { color: red; }"
}
And it could also be expanded to:
userstyle-metadata {
--name: "NewStyle-aefwe6zy";
--description: "This is your new file, start writing code";
--match: "<all_urls>";
--match-2: "*://*/*";
--match-3: "*://*.foo.bar/*";
--version: "1.0.0";
--download-url: "https://foo.bar/foo.user.css";
}
body {
--meta-uuid: "ac0d3cf8-d542-4830-abc9-94d851b6ff51";
--meta-name: "change color 1";
--meta-desc: "change the body color...";
color: red;
}
h1 {
--meta-uuid: "c393fdb0";
--meta-name: "change color 2";
color: red;
}
These additional metadata (will stripped by the manager), tracking the rulesets, and building a user interface to facilitate disabling some rulesets.
It's much easier than using comments and building a jsdoc-like parser.