@cssints/mise-en-mode 0.0.0-stage → 0.2.0
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/LICENSE +21 -0
- package/README.md +80 -2
- package/dist/index.d.mts +23 -0
- package/dist/index.mjs +37 -0
- package/package.json +30 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alexei Accio
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,81 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @cssints/mise-en-mode
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The look layer of Mise en Mode for [cssints](../../README.md): a **look** is a theme set on a `data-mode` scope, and looks
|
|
4
|
+
from three sources are resolved by the Mode Contract. It is one call member of `cssints/plugin` `scope()` (positional
|
|
5
|
+
arguments `$tokens` and `$overrides`, returning themes), so a look is checked against your token set, and `check.contrast` sees it.
|
|
6
|
+
No runtime.
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { createTokens } from "cssints" with { type: "cssints" };
|
|
10
|
+
import { look } from "@cssints/mise-en-mode" with { type: "cssints" };
|
|
11
|
+
|
|
12
|
+
const t = createTokens({
|
|
13
|
+
color: { $type: "color", bg: { $value: "#ffffff" }, fg: { $value: "#111111" } },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// look(tokens, expression, source, overrides, { scope? })
|
|
17
|
+
look(t, "dark", "app", { color: { bg: "#0b0b0f", fg: "#f3f4f6" } });
|
|
18
|
+
look(t, "dark", "viewer", { color: { bg: "#101018" } });
|
|
19
|
+
look(t, "hero", "author", { color: { bg: "#123456" } }, { scope: "article.post" });
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```css
|
|
23
|
+
@layer app, author, viewer;
|
|
24
|
+
@layer app {
|
|
25
|
+
:where([data-mode="dark"], [data-mode~="dark/color"]) {
|
|
26
|
+
--color-bg: #0b0b0f;
|
|
27
|
+
--color-fg: #f3f4f6;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
@layer viewer {
|
|
31
|
+
:where([data-mode="dark"], [data-mode~="dark/color"]) {
|
|
32
|
+
--color-bg: #101018;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
@layer author {
|
|
36
|
+
@scope (article.post) {
|
|
37
|
+
:where([data-mode="hero"], [data-mode~="hero/color"]) {
|
|
38
|
+
--color-bg: #123456;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## The contract, as the package does it
|
|
45
|
+
|
|
46
|
+
- **Sources (Rule 5).** `source` is `"app"`, `"author"` or `"viewer"`: the layers `app < author < viewer`, declared once
|
|
47
|
+
after the engine's own (`layers()` of cssints), so a look beats the engine's `:root` fallbacks and a viewer's look beats an
|
|
48
|
+
author's over the app's. Each variable fills from the most preferred source that sets it: the viewer look above sets only
|
|
49
|
+
`--color-bg`, and `--color-fg` still comes from the app look.
|
|
50
|
+
- **Aspects and fragments (Rule 4).** An aspect is a top-level group of the token set (`color`, `typography`). A look's
|
|
51
|
+
`overrides` are split by it, one rule per aspect, and each rule matches the bare marker or its fragment:
|
|
52
|
+
`data-mode="dark"` applies every aspect, `data-mode="dark/color"` only the color group.
|
|
53
|
+
- **Expressions.** `expression` is a `<custom-ident>` or a string; a dotted reverse-DNS key is quoted at the call,
|
|
54
|
+
`look(t, '"com.alice.midnight"', "author", …)`, and matched exactly.
|
|
55
|
+
- **Containment (Rule 7).** `{ scope: "article.post" }` wraps the rule in `@scope (article.post) { … }`, so a foreign look
|
|
56
|
+
cannot repaint the host's chrome. It is a selector, checked by the build.
|
|
57
|
+
- **Overrides** are `createGlobalTheme`'s: values by token path, each of the token's type, `{color.bg}` references allowed,
|
|
58
|
+
checked in the types as you type and again at build time (`color.bg value "blu" is not a <color>`, located at the call).
|
|
59
|
+
The aliases that depend on an overridden token are declared again on the scope.
|
|
60
|
+
- **Contrast.** The looks of one expression and aspect are one cascade to `check.contrast`: they are laid over each other in
|
|
61
|
+
layer order, so a viewer look that sets only the text colour is checked on the _app_ look's background, and a poor
|
|
62
|
+
combination fails the build.
|
|
63
|
+
|
|
64
|
+
A look prints no class: call it at a module's top level, like `createGlobalTheme`. Its types are inferred from the `scope()`
|
|
65
|
+
definition in `src/index.ts`: there is no types file.
|
|
66
|
+
|
|
67
|
+
Changed from `cssints/mise-en-mode`, which this package replaces: `look(expression, source, declarations, { scope })` took
|
|
68
|
+
raw custom-property declarations (`"--color-bg": "#000"`) that nothing checked; it is now `look(tokens, expression, source,
|
|
69
|
+
overrides, { scope })` with the token set first and the overrides by token path, so the declarations are checked, the
|
|
70
|
+
aliases are re-derived and contrast sees them. Aspects are the groups of the tokens, no longer a table of `--color-` and
|
|
71
|
+
`--typography-` prefixes, and a look emits themes, no longer globals.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
cd packages/mise-en-mode && bun run test # fixtures through the engine (output, errors, contrast), and the type test via bun run typecheck
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Credits
|
|
78
|
+
|
|
79
|
+
Mise en Mode is an open style standard on AT Protocol (<https://standard.mode.place>), and the Mode Contract is its
|
|
80
|
+
rules: `data-mode` scopes, the layers `app < author < viewer`, aspects. This package is an implementation of the
|
|
81
|
+
contract on `scope()`; no code of the standard's site is copied.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/index.d.ts
|
|
2
|
+
declare const mise: import("cssints/plugin").Scope<"mise-en-mode", {
|
|
3
|
+
readonly "#sources": {
|
|
4
|
+
readonly layers: readonly ["app", "author", "viewer"];
|
|
5
|
+
};
|
|
6
|
+
readonly look: {
|
|
7
|
+
readonly args: readonly ["$tokens", "<custom-ident> | <string>", "app | author | viewer", "$overrides", {
|
|
8
|
+
readonly "scope?": "$selector";
|
|
9
|
+
}];
|
|
10
|
+
readonly uses: readonly ["sources"];
|
|
11
|
+
};
|
|
12
|
+
}>;
|
|
13
|
+
/**
|
|
14
|
+
* `look(tokens, expression, source, overrides, { scope? })`: the theme of one `data-mode` expression, in the layer of its
|
|
15
|
+
* `source` (`"app"`, `"author"` or `"viewer"`), so a viewer's look fills over an author's over the app's, value by value.
|
|
16
|
+
* `overrides` are values by token path, split by their top-level groups (aspects): `data-mode="dark"` takes every aspect,
|
|
17
|
+
* `data-mode="dark/color"` only the color group. They are checked against `tokens` in the types and at build time, and
|
|
18
|
+
* `check.contrast` reads them with the other looks of the expression. With `scope` the look is confined to that subtree
|
|
19
|
+
* (`@scope`, Rule 7). A dotted reverse-DNS expression is quoted: `look(t, '"com.alice.midnight"', "author", { … })`. Call it
|
|
20
|
+
* at a module's top level; it prints no class.
|
|
21
|
+
*/
|
|
22
|
+
export declare const look: ReturnType<typeof mise.export>["look"];
|
|
23
|
+
//#endregion
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { scope } from "cssints/plugin";
|
|
2
|
+
//#region src/index.ts
|
|
3
|
+
const selector = (expression, aspect) => `:where([data-mode="${expression}"], [data-mode~="${expression}/${aspect}"])`;
|
|
4
|
+
/**
|
|
5
|
+
* `look(tokens, expression, source, overrides, { scope? })`: the theme of one `data-mode` expression, in the layer of its
|
|
6
|
+
* `source` (`"app"`, `"author"` or `"viewer"`), so a viewer's look fills over an author's over the app's, value by value.
|
|
7
|
+
* `overrides` are values by token path, split by their top-level groups (aspects): `data-mode="dark"` takes every aspect,
|
|
8
|
+
* `data-mode="dark/color"` only the color group. They are checked against `tokens` in the types and at build time, and
|
|
9
|
+
* `check.contrast` reads them with the other looks of the expression. With `scope` the look is confined to that subtree
|
|
10
|
+
* (`@scope`, Rule 7). A dotted reverse-DNS expression is quoted: `look(t, '"com.alice.midnight"', "author", { … })`. Call it
|
|
11
|
+
* at a module's top level; it prints no class.
|
|
12
|
+
*/
|
|
13
|
+
const look = scope("mise-en-mode", {
|
|
14
|
+
"#sources": { layers: [
|
|
15
|
+
"app",
|
|
16
|
+
"author",
|
|
17
|
+
"viewer"
|
|
18
|
+
] },
|
|
19
|
+
look: {
|
|
20
|
+
args: [
|
|
21
|
+
"$tokens",
|
|
22
|
+
"<custom-ident> | <string>",
|
|
23
|
+
"app | author | viewer",
|
|
24
|
+
"$overrides",
|
|
25
|
+
{ "scope?": "$selector" }
|
|
26
|
+
],
|
|
27
|
+
uses: ["sources"]
|
|
28
|
+
}
|
|
29
|
+
}).build({ look: (tokens, expression, source, overrides, options) => ({ themes: Object.entries(overrides).map(([aspect, values]) => ({
|
|
30
|
+
tokens,
|
|
31
|
+
overrides: { [aspect]: values },
|
|
32
|
+
selector: selector(expression.replace(/^"|"$/g, ""), aspect),
|
|
33
|
+
layer: source,
|
|
34
|
+
scope: options.scope
|
|
35
|
+
})) }) }).export().look;
|
|
36
|
+
//#endregion
|
|
37
|
+
export { look };
|
package/package.json
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cssints/mise-en-mode",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Mise en Mode for cssints: looks are themes in the layers app < author < viewer, set on data-mode scopes",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/alexeiaccio/css-in-ts-compiler.git",
|
|
9
|
+
"directory": "packages/mise-en-mode"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://cssints.dev",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.mts",
|
|
20
|
+
"default": "./dist/index.mjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"cssints": "^0.2.0"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=22"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
}
|
|
32
|
+
}
|