comark-colors 0.0.0 → 0.7.2

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 onmax
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,45 @@
1
1
  # comark-colors
2
2
 
3
- Placeholder. The first release is published from https://github.com/onmax/comark-plugins.
3
+ [![npm version](https://comark-ui.onmax.me/badge.svg?source=npm-version&package=comark-colors&icon=npm)](https://npmx.dev/package/comark-colors)
4
+ [![npm downloads](https://comark-ui.onmax.me/badge.svg?source=npm-downloads&package=comark-colors&period=month&icon=npm)](https://npmx.dev/package/comark-colors)
5
+ [![license](https://comark-ui.onmax.me/badge.svg?label=license&value=MIT)](https://npmx.dev/package/comark-colors)
6
+ [![docs](https://comark-ui.onmax.me/badge.svg?label=docs&value=comark-ui.onmax.me)](https://comark-ui.onmax.me/colors/)
7
+
8
+ A swatch before inline code that holds a color, as GitLab draws it, and `::palette` grids of named colors. A [Comark](https://comark.dev) plugin for HTML, Vue, React, and Svelte.
9
+
10
+ ```sh
11
+ pnpm add comark comark-colors
12
+ ```
13
+
14
+ ```ts
15
+ import { createHtmlRenderer } from "@comark/html";
16
+ import colors, { components } from "comark-colors/html";
17
+
18
+ const render = createHtmlRenderer({ plugins: [colors()], components });
19
+ ```
20
+
21
+ ```md
22
+ The button moves from `#3b82f6` to `oklch(0.55 0.2 264)`.
23
+
24
+ ::palette
25
+ - Primary: `#3b82f6`
26
+ - Danger: #dc2626
27
+ ::
28
+ ```
29
+
30
+ Inline code that holds one literal hex, `rgb()`, `hsl()`, `hwb()`, `lab()`, `lch()`, `oklab()`, or `oklch()` color gets a swatch, with no new syntax. Calculated components such as `calc()` are left as code. A `::palette` holds one list of `name: color` lines and draws a tile for each.
31
+
32
+ The plugin adds every problem with a palette to `meta.issues`: no list, content beside it, a line with no name or color, and a color it can't draw. Each issue names its palette and says how to fix it.
33
+
34
+ ## Renderers
35
+
36
+ | Renderer | Import |
37
+ | --- | --- |
38
+ | `@comark/html` | `comark-colors/html`: the styles are inlined once per document |
39
+ | `@comark/vue` | `comark-colors/vue` |
40
+ | `@comark/react` | `comark-colors/react` |
41
+ | `@comark/svelte` | `comark-colors/svelte` |
42
+
43
+ Each entry exports the parse plugin as `default` and the renderer's `components`. The Vue, React, and Svelte components add the styles when they mount; import `comark-colors/style.css` with your styles so a server-rendered page doesn't shift.
44
+
45
+ The styles follow shadcn/ui: they read its CSS variables, such as `--background` and `--border`, when the page has them, and use its neutral theme otherwise. Set the shared `--comark-*` tokens to theme every plugin at once; the rules sit in a `comark` CSS layer, so any plain rule of your own wins.
@@ -0,0 +1,12 @@
1
+ //#region src/styles.d.ts
2
+ /**
3
+ * A swatch inside inline code, and palettes as a grid of tiles in the style of shadcn/ui's Card, on the shared
4
+ * `--comark-*` tokens. Override `--cco-tile-width` and `--cco-tile-height` to size a palette's tiles. The rules sit in
5
+ * the `comark` layer, so any unlayered rule of the host's wins.
6
+ */
7
+ export declare const css: string;
8
+ //#endregion
9
+ //#region src/client.d.ts
10
+ /** Adds the colors styles to the page once. The Vue, React, and Svelte components call it when they mount. */
11
+ export declare function addStyles(): void;
12
+ //#endregion
@@ -0,0 +1,12 @@
1
+ import { t as css } from "./styles-gTSbKJ8j.mjs";
2
+ //#region src/client.ts
3
+ /** Adds the colors styles to the page once. The Vue, React, and Svelte components call it when they mount. */
4
+ function addStyles() {
5
+ if (document.querySelector("style[data-comark-colors]")) return;
6
+ const style = document.createElement("style");
7
+ style.dataset.comarkColors = "";
8
+ style.textContent = css;
9
+ document.head.append(style);
10
+ }
11
+ //#endregion
12
+ export { addStyles, css };
@@ -0,0 +1,7 @@
1
+ import _default, { ColorAttributes, ColorsMeta, isColor } from "./index.mjs";
2
+ import { NodeHandler } from "comark";
3
+ //#region src/html.d.ts
4
+ /** The HTML renderer for colors and `::palette`. Pass it as `components`; it inlines the styles once per document. */
5
+ export declare const components: Record<"color" | "palette", NodeHandler>;
6
+ //#endregion
7
+ export { type ColorAttributes, type ColorsMeta, _default as default, isColor };
package/dist/html.mjs ADDED
@@ -0,0 +1,27 @@
1
+ import src_default, { isColor } from "./index.mjs";
2
+ import { t as css } from "./styles-gTSbKJ8j.mjs";
3
+ import { escapeHtml } from "comark/utils";
4
+ //#region src/html.ts
5
+ const styled = /* @__PURE__ */ new WeakSet();
6
+ const attr = (value) => escapeHtml(value, { "\"": "&quot;" });
7
+ const str = (value) => value === void 0 ? "" : String(value);
8
+ const styles = (state) => {
9
+ const tag = styled.has(state.context) ? "" : `<style data-comark-colors>${css}</style>`;
10
+ styled.add(state.context);
11
+ return tag;
12
+ };
13
+ const color = ([, attrs], state) => {
14
+ const value = str(attrs.value);
15
+ const name = str(attrs.name);
16
+ if (!value) return "";
17
+ if (name) return `<li><span class="cco-tile" style="--cco:${attr(value)}"></span><span class="cco-name">${attr(name)}</span><span class="cco-value">${attr(value)}</span></li>`;
18
+ return `${styles(state)}<code class="cco"><span class="cco-swatch" style="--cco:${attr(value)}" aria-hidden="true"></span>${attr(value)}</code>`;
19
+ };
20
+ const palette = async ([, , ...children], state) => `${styles(state)}<ul class="cco-palette">${(await Promise.all(children.map((child) => state.one(child, state)))).join("")}</ul>`;
21
+ /** The HTML renderer for colors and `::palette`. Pass it as `components`; it inlines the styles once per document. */
22
+ const components = {
23
+ color,
24
+ palette
25
+ };
26
+ //#endregion
27
+ export { components, src_default as default, isColor };
@@ -0,0 +1,24 @@
1
+ //#region src/index.d.ts
2
+ export type ColorsMeta = {
3
+ /** One readable sentence per problem with a `::palette`. Empty when the document has none. Other plugins add theirs too. */
4
+ issues: string[];
5
+ /** The comark-linter rule of each issue, by its sentence, so the linter reports it under its own code. */
6
+ issueRules: Record<string, string>;
7
+ };
8
+ /**
9
+ * What a color becomes, and all its renderers read. Inline code that holds a color has only `value`; a `::palette` line
10
+ * has its `name` too.
11
+ */
12
+ export type ColorAttributes = {
13
+ value?: string;
14
+ name?: string;
15
+ };
16
+ /** Whether `value` is a hex, rgb(), hsl(), hwb(), lab(), lch(), oklab(), or oklch() color. */
17
+ export declare const isColor: (value: string) => boolean;
18
+ /**
19
+ * Puts a swatch before inline code that holds a color, such as `#3b82f6` or `oklch(0.62 0.19 260)`, and turns a
20
+ * `::palette` of `name: color` lines into a grid of swatches. Problems are added to `meta.issues`.
21
+ */
22
+ declare const _default: import("comark").ComarkPluginFactory<void, ColorsMeta, {}>;
23
+ //#endregion
24
+ export { _default as default };
package/dist/index.mjs ADDED
@@ -0,0 +1,100 @@
1
+ import { defineComarkPlugin } from "comark";
2
+ import { textContent } from "comark/utils";
3
+ //#region src/index.ts
4
+ const NUMBER = String.raw`[+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?`;
5
+ const PERCENT = String.raw`${NUMBER}%`;
6
+ const ANGLE = String.raw`${NUMBER}(?:deg|grad|rad|turn)`;
7
+ const NONE = String.raw`none`;
8
+ const CSS_WS = String.raw`(?:[ \t\r\n\f]|/\*(?:[^"'<>]|\r?\n)*?\*/)`;
9
+ const ALPHA = String.raw`(?:${NUMBER}|${PERCENT}|${NONE})`;
10
+ const RGB_CHANNEL = String.raw`(?:${NUMBER}|${PERCENT}|${NONE})`;
11
+ const HSL_HUE = String.raw`(?:${NUMBER}|${ANGLE}|${NONE})`;
12
+ const HSL_PERCENT = String.raw`(?:${PERCENT}|${NONE})`;
13
+ const LAB_CHANNEL = String.raw`(?:${NUMBER}|${PERCENT}|${NONE})`;
14
+ const LEGACY_RGB = String.raw`(?:${NUMBER}${CSS_WS}*,${CSS_WS}*${NUMBER}${CSS_WS}*,${CSS_WS}*${NUMBER}|${PERCENT}${CSS_WS}*,${CSS_WS}*${PERCENT}${CSS_WS}*,${CSS_WS}*${PERCENT})(?:${CSS_WS}*,${CSS_WS}*(?:${NUMBER}|${PERCENT}))?`;
15
+ const LEGACY_HSL = String.raw`(?:${NUMBER}|${ANGLE})`;
16
+ const MODERN = (a, b, c) => String.raw`${a}${CSS_WS}+${b}${CSS_WS}+${c}(?:${CSS_WS}*/${CSS_WS}*${ALPHA})?`;
17
+ const COLOR = new RegExp(String.raw`^(?:#(?:[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})|(?:rgb|rgba)\(${CSS_WS}*(?:${MODERN(RGB_CHANNEL, RGB_CHANNEL, RGB_CHANNEL)}|${LEGACY_RGB})${CSS_WS}*\)|(?:hsl|hsla)\(${CSS_WS}*(?:${MODERN(HSL_HUE, HSL_PERCENT, HSL_PERCENT)}|${LEGACY_HSL}${CSS_WS}*,${CSS_WS}*${PERCENT}${CSS_WS}*,${CSS_WS}*${PERCENT}(?:${CSS_WS}*,${CSS_WS}*(?:${NUMBER}|${PERCENT}))?)${CSS_WS}*\)|hwb\(${CSS_WS}*${MODERN(HSL_HUE, HSL_PERCENT, HSL_PERCENT)}${CSS_WS}*\)|lab\(${CSS_WS}*${MODERN(LAB_CHANNEL, LAB_CHANNEL, LAB_CHANNEL)}${CSS_WS}*\)|lch\(${CSS_WS}*${MODERN(LAB_CHANNEL, LAB_CHANNEL, HSL_HUE)}${CSS_WS}*\)|oklab\(${CSS_WS}*${MODERN(LAB_CHANNEL, LAB_CHANNEL, LAB_CHANNEL)}${CSS_WS}*\)|oklch\(${CSS_WS}*${MODERN(LAB_CHANNEL, LAB_CHANNEL, HSL_HUE)}${CSS_WS}*\))$`, "i");
18
+ const EXAMPLE = "`- Primary: #3b82f6`";
19
+ /** Whether `value` is a hex, rgb(), hsl(), hwb(), lab(), lch(), oklab(), or oklch() color. */
20
+ const isColor = (value) => COLOR.test(value);
21
+ const isElement = (node) => Array.isArray(node) && node[0] !== null;
22
+ const childrenOf = ([, , ...rest]) => rest;
23
+ const isContent = (node) => Array.isArray(node) ? node[0] !== null : node.trim() !== "";
24
+ function palette(node, number, issues) {
25
+ const name = `\`::palette\` ${number}`;
26
+ for (const key of Object.keys(node[1])) {
27
+ const prop = key.replace(/^:/, "");
28
+ if (prop !== "id" && prop !== "class") issues.push({
29
+ rule: "comark/colors/unknown-prop",
30
+ message: `${name}: \`${prop}\` isn't a prop of \`::palette\`, which has none, so it's ignored. Remove it.`
31
+ });
32
+ }
33
+ const content = childrenOf(node).filter(isContent);
34
+ const lists = content.filter((child) => isElement(child) && (child[0] === "ul" || child[0] === "ol"));
35
+ if (!lists.length) {
36
+ issues.push({
37
+ rule: "comark/colors/missing-list",
38
+ message: `${name} needs a list of \`name: color\` lines, such as ${EXAMPLE}.`
39
+ });
40
+ return ["palette", {}];
41
+ }
42
+ if (lists.length < content.length) issues.push({
43
+ rule: "comark/colors/content-dropped",
44
+ message: `${name} drops everything but its list. Move the rest outside the block.`
45
+ });
46
+ const colors = [];
47
+ for (const item of lists.flatMap(childrenOf).filter(isElement)) {
48
+ const line = textContent(item).trim();
49
+ const [, label, value = ""] = /^(.*\S)\s*:\s*(\S.*)$/s.exec(line) ?? [];
50
+ if (!label) issues.push({
51
+ rule: "comark/colors/unreadable-line",
52
+ message: `${name}: "${line}" needs a name and a color, such as ${EXAMPLE}.`
53
+ });
54
+ else if (!isColor(value.trim())) issues.push({
55
+ rule: "comark/colors/invalid-color",
56
+ message: `${name}: "${value.trim()}" for ${label} isn't a color it can draw. Write a hex color or an rgb(), hsl(), hwb(), lab(), lch(), oklab(), or oklch() color.`
57
+ });
58
+ else colors.push(["color", {
59
+ name: label,
60
+ value: value.trim()
61
+ }]);
62
+ }
63
+ return [
64
+ "palette",
65
+ {},
66
+ ...colors
67
+ ];
68
+ }
69
+ /**
70
+ * Puts a swatch before inline code that holds a color, such as `#3b82f6` or `oklch(0.62 0.19 260)`, and turns a
71
+ * `::palette` of `name: color` lines into a grid of swatches. Problems are added to `meta.issues`.
72
+ */
73
+ var src_default = defineComarkPlugin(() => ({
74
+ name: "comark-colors",
75
+ post(state) {
76
+ const { tree } = state;
77
+ const issues = [];
78
+ let count = 0;
79
+ const walk = (nodes, start) => {
80
+ for (let index = start; index < nodes.length; index++) {
81
+ const node = nodes[index];
82
+ if (!node || !isElement(node) || node[0] === "pre") continue;
83
+ if (node[0] === "palette") nodes[index] = palette(node, ++count, issues);
84
+ else if (node[0] !== "code") walk(node, 2);
85
+ else {
86
+ const [value, ...rest] = childrenOf(node);
87
+ if (value !== void 0 && !Array.isArray(value) && !rest.length && isColor(value.trim())) nodes[index] = ["color", { value: value.trim() }];
88
+ }
89
+ }
90
+ };
91
+ walk(tree.nodes, 0);
92
+ tree.meta.issues = [...tree.meta.issues ?? [], ...issues.map((issue) => issue.message)];
93
+ tree.meta.issueRules = {
94
+ ...tree.meta.issueRules,
95
+ ...Object.fromEntries(issues.map((issue) => [issue.message, issue.rule]))
96
+ };
97
+ }
98
+ }));
99
+ //#endregion
100
+ export { src_default as default, isColor };
@@ -0,0 +1,20 @@
1
+ import _default, { ColorAttributes, ColorsMeta, isColor } from "./index.mjs";
2
+ import { ReactNode } from "react";
3
+ //#region src/react.d.ts
4
+ /** A color in inline code, or a line of a `::palette`, for `@comark/react`. */
5
+ export declare function Color({ value, name }: ColorAttributes): import("react").DetailedReactHTMLElement<import("react").HTMLAttributes<HTMLElement>, HTMLElement> | import("react").DetailedReactHTMLElement<{
6
+ className: string;
7
+ }, HTMLElement> | null;
8
+ /** `::palette` for `@comark/react`. */
9
+ export declare function Palette({ children }: {
10
+ children?: ReactNode;
11
+ }): import("react").DetailedReactHTMLElement<{
12
+ className: string;
13
+ }, HTMLElement>;
14
+ /** Pass these as `components` to `@comark/react`. */
15
+ export declare const components: {
16
+ Color: typeof Color;
17
+ Palette: typeof Palette;
18
+ };
19
+ //#endregion
20
+ export { type ColorAttributes, type ColorsMeta, _default as default, isColor };
package/dist/react.mjs ADDED
@@ -0,0 +1,31 @@
1
+ import src_default, { isColor } from "./index.mjs";
2
+ import { addStyles } from "./client.mjs";
3
+ import { createElement, useEffect } from "react";
4
+ //#region src/react.ts
5
+ /** A color in inline code, or a line of a `::palette`, for `@comark/react`. */
6
+ function Color({ value, name }) {
7
+ useEffect(addStyles, []);
8
+ if (!value) return null;
9
+ const style = { "--cco": value };
10
+ if (name) return createElement("li", null, createElement("span", {
11
+ className: "cco-tile",
12
+ style
13
+ }), createElement("span", { className: "cco-name" }, name), createElement("span", { className: "cco-value" }, value));
14
+ return createElement("code", { className: "cco" }, createElement("span", {
15
+ className: "cco-swatch",
16
+ style,
17
+ "aria-hidden": true
18
+ }), value);
19
+ }
20
+ /** `::palette` for `@comark/react`. */
21
+ function Palette({ children }) {
22
+ useEffect(addStyles, []);
23
+ return createElement("ul", { className: "cco-palette" }, children);
24
+ }
25
+ /** Pass these as `components` to `@comark/react`. */
26
+ const components = {
27
+ Color,
28
+ Palette
29
+ };
30
+ //#endregion
31
+ export { Color, Palette, components, src_default as default, isColor };
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ @layer comark{:where(.cco-palette,.cco){--_comark-background:var(--comark-background,var(--ui-bg,var(--background,#fff)));--_comark-foreground:var(--comark-foreground,var(--ui-text-highlighted,var(--foreground,#0a0a0a)));--_comark-muted:var(--comark-muted,var(--ui-text-muted,var(--muted-foreground,#737373)));--_comark-border:var(--comark-border,var(--ui-border,var(--border,#e5e5e5)));--_comark-accent:var(--comark-accent,var(--ui-bg-elevated,var(--accent,#f5f5f5)));--_comark-ring:var(--comark-ring,var(--ui-primary,var(--ring,#a1a1a1)));--_comark-radius:var(--comark-radius,calc(var(--ui-radius,calc(var(--radius,.625rem)/2))*2));--_comark-font:var(--comark-font,var(--font-sans,ui-sans-serif,system-ui,sans-serif))}@media (prefers-color-scheme:dark){:where(.cco-palette,.cco){--_comark-background:var(--comark-background,var(--ui-bg,var(--background,#0a0a0a)));--_comark-foreground:var(--comark-foreground,var(--ui-text-highlighted,var(--foreground,#fafafa)));--_comark-muted:var(--comark-muted,var(--ui-text-muted,var(--muted-foreground,#a1a1a1)));--_comark-border:var(--comark-border,var(--ui-border,var(--border,rgb(255 255 255/.1))));--_comark-accent:var(--comark-accent,var(--ui-bg-elevated,var(--accent,#262626)));--_comark-ring:var(--comark-ring,var(--ui-primary,var(--ring,#737373)))}}:where(.cco-palette){--cco-tile-width:9rem;--cco-tile-height:4rem}.cco-swatch{display:inline-block;box-sizing:border-box;width:.8em;height:.8em;margin-inline-end:.35em;vertical-align:-.05em;border:1px solid var(--_comark-border);border-radius:.2em;background:linear-gradient(var(--cco),var(--cco)),repeating-conic-gradient(color-mix(in srgb,var(--_comark-muted) 35%,var(--_comark-background)) 0 25%,var(--_comark-background) 0 50%) 0 0/8px 8px}.cco-palette{display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--cco-tile-width),1fr));gap:.75rem;margin:1.5rem 0;padding:0;list-style:none;font-family:var(--_comark-font)}.cco-palette>li{margin:0!important;padding:0!important;overflow:hidden;border:1px solid var(--_comark-border);border-radius:var(--_comark-radius);background:var(--_comark-background);color:var(--_comark-foreground)}.cco-tile{display:block;height:var(--cco-tile-height);border-bottom:1px solid var(--_comark-border);background:linear-gradient(var(--cco),var(--cco)),repeating-conic-gradient(color-mix(in srgb,var(--_comark-muted) 35%,var(--_comark-background)) 0 25%,var(--_comark-background) 0 50%) 0 0/8px 8px}.cco-name,.cco-value{display:block;padding:0 .75rem;line-height:1.25rem;overflow-wrap:anywhere}.cco-name{padding-top:.5rem;font-size:.875rem;font-weight:500}.cco-value{padding-bottom:.5rem;color:var(--_comark-muted);font:.75rem/1.25rem ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.cco-swatch,.cco-tile{-webkit-print-color-adjust:exact;print-color-adjust:exact}@media print{.cco-palette>li{break-inside:avoid}}}
@@ -0,0 +1,79 @@
1
+ //#region ../zoom/dist/theme.mjs
2
+ const tokens = [
3
+ [
4
+ "background",
5
+ "ui-bg",
6
+ "background",
7
+ "#fff",
8
+ "#0a0a0a"
9
+ ],
10
+ [
11
+ "foreground",
12
+ "ui-text-highlighted",
13
+ "foreground",
14
+ "#0a0a0a",
15
+ "#fafafa"
16
+ ],
17
+ [
18
+ "muted",
19
+ "ui-text-muted",
20
+ "muted-foreground",
21
+ "#737373",
22
+ "#a1a1a1"
23
+ ],
24
+ [
25
+ "border",
26
+ "ui-border",
27
+ "border",
28
+ "#e5e5e5",
29
+ "rgb(255 255 255/.1)"
30
+ ],
31
+ [
32
+ "accent",
33
+ "ui-bg-elevated",
34
+ "accent",
35
+ "#f5f5f5",
36
+ "#262626"
37
+ ],
38
+ [
39
+ "ring",
40
+ "ui-primary",
41
+ "ring",
42
+ "#a1a1a1",
43
+ "#737373"
44
+ ]
45
+ ];
46
+ const resolve = (scheme) => tokens.map((token) => `--_comark-${token[0]}:var(--comark-${token[0]},var(--${token[1]},var(--${token[2]},${token[scheme]})))`).join(";");
47
+ /**
48
+ * Resolves the `--comark-*` tokens on `selector`, falling back to Nuxt UI's variables, then shadcn/ui's, then shadcn's
49
+ * neutral theme. Nuxt UI's corners are twice its `--ui-radius`, so the radius halves shadcn's to double it back.
50
+ * They resolve on the component rather than `:root`, so a `.dark` class anywhere above it switches the theme, and a
51
+ * `--comark-*` set on any ancestor still inherits down. `:where()` keeps them at zero specificity.
52
+ */
53
+ function theme(selector) {
54
+ const where = `:where(${selector})`;
55
+ return `${where}{${resolve(3)};--_comark-radius:var(--comark-radius,calc(var(--ui-radius,calc(var(--radius,.625rem)/2))*2));--_comark-font:var(--comark-font,var(--font-sans,ui-sans-serif,system-ui,sans-serif))}@media (prefers-color-scheme:dark){${where}{${resolve(4)}}}`;
56
+ }
57
+ //#endregion
58
+ //#region src/styles.ts
59
+ const fill = "background:linear-gradient(var(--cco),var(--cco)),repeating-conic-gradient(color-mix(in srgb,var(--_comark-muted) 35%,var(--_comark-background)) 0 25%,var(--_comark-background) 0 50%) 0 0/8px 8px";
60
+ /**
61
+ * A swatch inside inline code, and palettes as a grid of tiles in the style of shadcn/ui's Card, on the shared
62
+ * `--comark-*` tokens. Override `--cco-tile-width` and `--cco-tile-height` to size a palette's tiles. The rules sit in
63
+ * the `comark` layer, so any unlayered rule of the host's wins.
64
+ */
65
+ const css = `@layer comark{${[
66
+ theme(".cco-palette,.cco"),
67
+ ":where(.cco-palette){--cco-tile-width:9rem;--cco-tile-height:4rem}",
68
+ `.cco-swatch{display:inline-block;box-sizing:border-box;width:.8em;height:.8em;margin-inline-end:.35em;vertical-align:-.05em;border:1px solid var(--_comark-border);border-radius:.2em;${fill}}`,
69
+ ".cco-palette{display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--cco-tile-width),1fr));gap:.75rem;margin:1.5rem 0;padding:0;list-style:none;font-family:var(--_comark-font)}",
70
+ ".cco-palette>li{margin:0!important;padding:0!important;overflow:hidden;border:1px solid var(--_comark-border);border-radius:var(--_comark-radius);background:var(--_comark-background);color:var(--_comark-foreground)}",
71
+ `.cco-tile{display:block;height:var(--cco-tile-height);border-bottom:1px solid var(--_comark-border);${fill}}`,
72
+ ".cco-name,.cco-value{display:block;padding:0 .75rem;line-height:1.25rem;overflow-wrap:anywhere}",
73
+ ".cco-name{padding-top:.5rem;font-size:.875rem;font-weight:500}",
74
+ ".cco-value{padding-bottom:.5rem;color:var(--_comark-muted);font:.75rem/1.25rem ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}",
75
+ ".cco-swatch,.cco-tile{-webkit-print-color-adjust:exact;print-color-adjust:exact}",
76
+ "@media print{.cco-palette>li{break-inside:avoid}}"
77
+ ].join("")}}`;
78
+ //#endregion
79
+ export { css as t };
@@ -0,0 +1,14 @@
1
+ <!-- A color in inline code, or a line of a `::palette`, for `@comark/svelte`. -->
2
+ <script>
3
+ import { onMount } from "svelte";
4
+ import { addStyles } from "comark-colors/client";
5
+
6
+ let { value = undefined, name = undefined } = $props();
7
+ onMount(addStyles);
8
+ </script>
9
+
10
+ {#if value && name}
11
+ <li><span class="cco-tile" style:--cco={value}></span><span class="cco-name">{name}</span><span class="cco-value">{value}</span></li>
12
+ {:else if value}
13
+ <code class="cco"><span class="cco-swatch" style:--cco={value} aria-hidden="true"></span>{value}</code>
14
+ {/if}
@@ -0,0 +1,10 @@
1
+ <!-- `::palette` for `@comark/svelte`. -->
2
+ <script>
3
+ import { onMount } from "svelte";
4
+ import { addStyles } from "comark-colors/client";
5
+
6
+ let { children } = $props();
7
+ onMount(addStyles);
8
+ </script>
9
+
10
+ <ul class="cco-palette">{@render children?.()}</ul>
@@ -0,0 +1,9 @@
1
+ import type { Component, Snippet } from "svelte";
2
+ import type { ColorAttributes } from "comark-colors";
3
+
4
+ export { default, isColor } from "comark-colors";
5
+ export type { ColorAttributes, ColorsMeta } from "comark-colors";
6
+
7
+ export declare const Color: Component<ColorAttributes>;
8
+ export declare const Palette: Component<{ children?: Snippet }>;
9
+ export declare const components: { Color: typeof Color; Palette: typeof Palette };
@@ -0,0 +1,7 @@
1
+ // Colors and `::palette` for `@comark/svelte`. Pass `components` to it; the parse plugin comes from `comark-colors`.
2
+ import Color from "./Color.svelte";
3
+ import Palette from "./Palette.svelte";
4
+
5
+ export { default, isColor } from "comark-colors";
6
+ export { Color, Palette };
7
+ export const components = { Color, Palette };
package/dist/vue.d.mts ADDED
@@ -0,0 +1,33 @@
1
+ import _default, { ColorAttributes, ColorsMeta, isColor } from "./index.mjs";
2
+ //#region src/vue.d.ts
3
+ /** A color in inline code, or a line of a `::palette`, for `@comark/vue`. */
4
+ export declare const Color: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
5
+ value: StringConstructor;
6
+ name: StringConstructor;
7
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
8
+ [key: string]: any;
9
+ }> | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
10
+ value: StringConstructor;
11
+ name: StringConstructor;
12
+ }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
13
+ /** `::palette` for `@comark/vue`. */
14
+ export declare const Palette: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
15
+ [key: string]: any;
16
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
17
+ /** Pass these as `components` to `@comark/vue`. */
18
+ export declare const components: {
19
+ Color: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
20
+ value: StringConstructor;
21
+ name: StringConstructor;
22
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
23
+ [key: string]: any;
24
+ }> | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
25
+ value: StringConstructor;
26
+ name: StringConstructor;
27
+ }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
28
+ Palette: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
29
+ [key: string]: any;
30
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
31
+ };
32
+ //#endregion
33
+ export { type ColorAttributes, type ColorsMeta, _default as default, isColor };
package/dist/vue.mjs ADDED
@@ -0,0 +1,49 @@
1
+ import src_default, { isColor } from "./index.mjs";
2
+ import { addStyles } from "./client.mjs";
3
+ import { defineComponent, h, onMounted } from "vue";
4
+ //#region src/vue.ts
5
+ /** A color in inline code, or a line of a `::palette`, for `@comark/vue`. */
6
+ const Color = defineComponent({
7
+ name: "Color",
8
+ inheritAttrs: false,
9
+ props: {
10
+ value: String,
11
+ name: String
12
+ },
13
+ setup(props) {
14
+ onMounted(addStyles);
15
+ return () => {
16
+ if (!props.value) return null;
17
+ const style = { "--cco": props.value };
18
+ if (props.name) return h("li", [
19
+ h("span", {
20
+ class: "cco-tile",
21
+ style
22
+ }),
23
+ h("span", { class: "cco-name" }, props.name),
24
+ h("span", { class: "cco-value" }, props.value)
25
+ ]);
26
+ return h("code", { class: "cco" }, [h("span", {
27
+ class: "cco-swatch",
28
+ style,
29
+ "aria-hidden": "true"
30
+ }), props.value]);
31
+ };
32
+ }
33
+ });
34
+ /** `::palette` for `@comark/vue`. */
35
+ const Palette = defineComponent({
36
+ name: "Palette",
37
+ inheritAttrs: false,
38
+ setup(_, { slots }) {
39
+ onMounted(addStyles);
40
+ return () => h("ul", { class: "cco-palette" }, slots.default?.());
41
+ }
42
+ });
43
+ /** Pass these as `components` to `@comark/vue`. */
44
+ const components = {
45
+ Color,
46
+ Palette
47
+ };
48
+ //#endregion
49
+ export { Color, Palette, components, src_default as default, isColor };
package/package.json CHANGED
@@ -1,12 +1,90 @@
1
1
  {
2
2
  "name": "comark-colors",
3
- "version": "0.0.0",
4
- "description": "Placeholder. The first release is published from https://github.com/onmax/comark-plugins.",
3
+ "version": "0.7.2",
4
+ "description": "Color swatches next to inline code that holds a color, like GitLab, and ::palette grids of named colors. A Comark plugin.",
5
+ "keywords": [
6
+ "color",
7
+ "comark",
8
+ "markdown",
9
+ "mdc",
10
+ "palette",
11
+ "plugin",
12
+ "swatch"
13
+ ],
14
+ "homepage": "https://comark-ui.onmax.me/colors",
15
+ "bugs": "https://github.com/onmax/comark-ui/issues",
5
16
  "license": "MIT",
6
17
  "author": "onmax",
7
18
  "repository": {
8
19
  "type": "git",
9
- "url": "git+https://github.com/onmax/comark-plugins.git",
20
+ "url": "git+https://github.com/onmax/comark-ui.git",
10
21
  "directory": "packages/colors"
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "type": "module",
27
+ "sideEffects": [
28
+ "*.css"
29
+ ],
30
+ "exports": {
31
+ ".": "./dist/index.mjs",
32
+ "./client": "./dist/client.mjs",
33
+ "./html": "./dist/html.mjs",
34
+ "./react": "./dist/react.mjs",
35
+ "./style.css": "./dist/style.css",
36
+ "./svelte": {
37
+ "types": "./dist/svelte/index.d.ts",
38
+ "svelte": "./dist/svelte/index.js",
39
+ "default": "./dist/svelte/index.js"
40
+ },
41
+ "./vue": "./dist/vue.mjs",
42
+ "./package.json": "./package.json"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public",
46
+ "provenance": true
47
+ },
48
+ "devDependencies": {
49
+ "@comark/html": "^0.8.1",
50
+ "@comark/react": "^0.8.1",
51
+ "@comark/vue": "^0.8.1",
52
+ "@types/react": "^19.3.0",
53
+ "@types/react-dom": "^19.3.0",
54
+ "comark": "^0.8.1",
55
+ "react": "^19.3.1",
56
+ "react-dom": "^19.3.1",
57
+ "svelte": "^5.57.2",
58
+ "vue": "^3.5.44",
59
+ "comark-zoom": "0.7.2"
60
+ },
61
+ "peerDependencies": {
62
+ "@comark/html": "^0.8.0",
63
+ "comark": "^0.8.0",
64
+ "react": ">=18",
65
+ "svelte": ">=5",
66
+ "vue": ">=3.5"
67
+ },
68
+ "peerDependenciesMeta": {
69
+ "@comark/html": {
70
+ "optional": true
71
+ },
72
+ "react": {
73
+ "optional": true
74
+ },
75
+ "svelte": {
76
+ "optional": true
77
+ },
78
+ "vue": {
79
+ "optional": true
80
+ }
81
+ },
82
+ "engines": {
83
+ "node": ">=22.12.0"
84
+ },
85
+ "scripts": {
86
+ "build": "tsdown",
87
+ "dev": "tsdown --watch",
88
+ "doctor": "vite-doctor . --no-cache --max-warnings 0 --format text"
11
89
  }
12
- }
90
+ }