@telegraph/postcss-config 0.0.31 → 0.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @telegraph/postcss-config
2
2
 
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#714](https://github.com/knocklabs/telegraph/pull/714) [`627e61c`](https://github.com/knocklabs/telegraph/commit/627e61c3b17ccfc36f5fb835bb5f21a092efca95) Thanks [@kylemcd](https://github.com/kylemcd)! - feat: hover, focus, etc states as component props
8
+
3
9
  ## 0.0.31
4
10
 
5
11
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { default as styleEnginePostCssConfig } from "./style-engine-postcss-config";
2
+ export { default as interactivePlugin } from "./interactive-plugin";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,+BAA+B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -3,6 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.styleEnginePostCssConfig = void 0;
6
+ exports.interactivePlugin = exports.styleEnginePostCssConfig = void 0;
7
7
  var style_engine_postcss_config_1 = require("./style-engine-postcss-config");
8
8
  Object.defineProperty(exports, "styleEnginePostCssConfig", { enumerable: true, get: function () { return __importDefault(style_engine_postcss_config_1).default; } });
9
+ var interactive_plugin_1 = require("./interactive-plugin");
10
+ Object.defineProperty(exports, "interactivePlugin", { enumerable: true, get: function () { return __importDefault(interactive_plugin_1).default; } });
@@ -0,0 +1,29 @@
1
+ import type { AcceptedPlugin } from "postcss";
2
+ /**
3
+ * PostCSS plugin: @telegraph/interactive
4
+ *
5
+ * Processes `@telegraph interactive(<selector>)` at-rules and auto-generates
6
+ * pseudo-class cascade rules based on the base rule's declarations.
7
+ *
8
+ * Input:
9
+ * .tgph-box {
10
+ * --background-color: none;
11
+ * background-color: var(--background-color);
12
+ * border-color: var(--border-color);
13
+ * }
14
+ * @telegraph interactive(.tgph-box);
15
+ *
16
+ * Output (appended):
17
+ * .tgph-box--interactive:hover {
18
+ * background-color: var(--hover--background-color, var(--background-color));
19
+ * border-color: var(--hover--border-color, var(--border-color));
20
+ * }
21
+ * .tgph-box--interactive:focus-visible { ... }
22
+ * .tgph-box--interactive:active { ... }
23
+ * .tgph-box--interactive:has(:focus-within) { ... }
24
+ * .tgph-box--interactive:disabled,
25
+ * .tgph-box--interactive[aria-disabled="true"] { ... }
26
+ */
27
+ declare const interactivePlugin: () => AcceptedPlugin;
28
+ export default interactivePlugin;
29
+ //# sourceMappingURL=interactive-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive-plugin.d.ts","sourceRoot":"","sources":["../src/interactive-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA6B,MAAM,SAAS,CAAC;AAoFzE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,QAAA,MAAM,iBAAiB,QAAO,cA6E7B,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Mapping from pseudo-state names to the CSS variable prefix used in
5
+ * the cascade-fallback pattern.
6
+ *
7
+ * This must stay in sync with PSEUDO_CSS_PREFIX in
8
+ * packages/style-engine/src/helpers/getStyleProp/getStyleProp.ts
9
+ */
10
+ const PSEUDO_STATE_MAP = {
11
+ hover: "hover",
12
+ focus: "focus",
13
+ active: "active",
14
+ "focus-within": "focus-within",
15
+ disabled: "disabled",
16
+ };
17
+ /**
18
+ * Maps pseudo-state names to their CSS selectors.
19
+ * Each state may have multiple selector variants (e.g. disabled also matches
20
+ * aria-disabled).
21
+ */
22
+ const PSEUDO_SELECTORS = {
23
+ hover: [":hover"],
24
+ focus: [":focus-visible"],
25
+ active: [":active"],
26
+ "focus-within": [":has(:focus-within)"],
27
+ disabled: [":disabled", '[aria-disabled="true"]'],
28
+ };
29
+ /**
30
+ * Extracts declarations from a rule that follow the pattern:
31
+ * property: var(--custom-prop);
32
+ *
33
+ * These are the declarations we'll generate pseudo-state variants for.
34
+ * We only match simple `var(--name)` references (no fallbacks in the base rule)
35
+ * since those are the style-engine-managed properties.
36
+ */
37
+ function extractInteractiveDeclarations(rule) {
38
+ const declarations = [];
39
+ rule.walkDecls((decl) => {
40
+ // Skip custom property definitions (e.g. --background-color: none).
41
+ // We only want real CSS property declarations like `background-color: var(--background-color)`.
42
+ if (decl.prop.startsWith("--"))
43
+ return;
44
+ // Match `var(--some-name)` — simple variable reference with no fallback
45
+ const match = decl.value.match(/^var\((--[\w-]+)\)$/);
46
+ if (match && match[1]) {
47
+ declarations.push({
48
+ property: decl.prop,
49
+ cssVar: match[1],
50
+ });
51
+ }
52
+ });
53
+ return declarations;
54
+ }
55
+ /**
56
+ * Builds the pseudo-state selector string for a given base selector and state.
57
+ *
58
+ * Examples:
59
+ * buildSelector(".tgph-box", "hover")
60
+ * => ".tgph-box--interactive:hover"
61
+ * buildSelector(".tgph-box", "disabled")
62
+ * => ".tgph-box--interactive:disabled, .tgph-box--interactive[aria-disabled=\"true\"]"
63
+ */
64
+ function buildSelector(baseSelector, state) {
65
+ const pseudoSelectors = PSEUDO_SELECTORS[state];
66
+ if (!pseudoSelectors)
67
+ return "";
68
+ return pseudoSelectors
69
+ .map((pseudo) => `${baseSelector}--interactive${pseudo}`)
70
+ .join(",\n");
71
+ }
72
+ /**
73
+ * PostCSS plugin: @telegraph/interactive
74
+ *
75
+ * Processes `@telegraph interactive(<selector>)` at-rules and auto-generates
76
+ * pseudo-class cascade rules based on the base rule's declarations.
77
+ *
78
+ * Input:
79
+ * .tgph-box {
80
+ * --background-color: none;
81
+ * background-color: var(--background-color);
82
+ * border-color: var(--border-color);
83
+ * }
84
+ * @telegraph interactive(.tgph-box);
85
+ *
86
+ * Output (appended):
87
+ * .tgph-box--interactive:hover {
88
+ * background-color: var(--hover--background-color, var(--background-color));
89
+ * border-color: var(--hover--border-color, var(--border-color));
90
+ * }
91
+ * .tgph-box--interactive:focus-visible { ... }
92
+ * .tgph-box--interactive:active { ... }
93
+ * .tgph-box--interactive:has(:focus-within) { ... }
94
+ * .tgph-box--interactive:disabled,
95
+ * .tgph-box--interactive[aria-disabled="true"] { ... }
96
+ */
97
+ const interactivePlugin = () => {
98
+ return {
99
+ postcssPlugin: "@telegraph/interactive",
100
+ Once(root) {
101
+ // Collect all @telegraph interactive(...) directives
102
+ const directives = [];
103
+ root.walkAtRules("telegraph", (atRule) => {
104
+ const match = atRule.params.match(/^interactive\((.+)\)$/);
105
+ if (match && match[1]) {
106
+ directives.push({
107
+ atRule,
108
+ selector: match[1].trim(),
109
+ });
110
+ }
111
+ });
112
+ if (directives.length === 0)
113
+ return;
114
+ for (const { atRule, selector } of directives) {
115
+ // Find the base rule matching the selector
116
+ let baseRule = null;
117
+ root.walkRules((rule) => {
118
+ if (rule.selector === selector) {
119
+ baseRule = rule;
120
+ }
121
+ });
122
+ if (!baseRule) {
123
+ throw atRule.error(`@telegraph interactive: could not find base rule "${selector}" in this file.`);
124
+ }
125
+ const declarations = extractInteractiveDeclarations(baseRule);
126
+ if (declarations.length === 0) {
127
+ throw atRule.error(`@telegraph interactive: no "property: var(--name)" declarations found in "${selector}".`);
128
+ }
129
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
130
+ const postcss = require("postcss");
131
+ // Track the last inserted node so rules are appended in order
132
+ let lastInserted = atRule;
133
+ // Generate pseudo-state rules for each state
134
+ for (const state of Object.keys(PSEUDO_STATE_MAP)) {
135
+ const prefix = PSEUDO_STATE_MAP[state];
136
+ const pseudoSelector = buildSelector(selector, state);
137
+ if (!pseudoSelector)
138
+ continue;
139
+ const rule = postcss.rule({ selector: pseudoSelector });
140
+ for (const decl of declarations) {
141
+ rule.append(postcss.decl({
142
+ prop: decl.property,
143
+ value: `var(--${prefix}--${decl.cssVar.replace(/^--/, "")}, var(${decl.cssVar}))`,
144
+ }));
145
+ }
146
+ // Insert after the last inserted node to preserve order
147
+ atRule.parent?.insertAfter(lastInserted, rule);
148
+ lastInserted = rule;
149
+ }
150
+ // Remove the directive
151
+ atRule.remove();
152
+ }
153
+ },
154
+ };
155
+ };
156
+ exports.default = interactivePlugin;
@@ -1 +1 @@
1
- {"version":3,"file":"style-engine-postcss-config.d.ts","sourceRoot":"","sources":["../src/style-engine-postcss-config.ts"],"names":[],"mappings":";;;AAAA,wBAUE"}
1
+ {"version":3,"file":"style-engine-postcss-config.d.ts","sourceRoot":"","sources":["../src/style-engine-postcss-config.ts"],"names":[],"mappings":";;;AAGA,wBAgBE"}
@@ -1,13 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
4
+ const interactivePlugin = require("./interactive-plugin").default;
3
5
  exports.default = {
4
6
  plugins: [
7
+ // Must run before other plugins so that the generated rules are available
8
+ // for subsequent processing (e.g. autoprefixer, discard-empty).
9
+ interactivePlugin(),
10
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
5
11
  require("postcss-discard-empty"),
12
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
6
13
  require("autoprefixer"),
7
- // eslint-disable-next-line @typescript-eslint/no-var-requires
14
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
8
15
  require("@csstools/postcss-global-data")({
9
16
  module: ["@telegraph/tokens/dist/css/breakpoints.css"],
10
17
  }),
18
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
11
19
  require("postcss-custom-media"),
12
20
  ],
13
21
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telegraph/postcss-config",
3
- "version": "0.0.31",
3
+ "version": "0.1.0",
4
4
  "author": "@knocklabs",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/knocklabs/telegraph/tree/main/packages/postcss-config",
@@ -18,8 +18,8 @@
18
18
  "format:check": "prettier \"src/**/*.{js,ts,tsx}\" --check"
19
19
  },
20
20
  "dependencies": {
21
- "@csstools/postcss-global-data": "^3.1.0",
22
- "@telegraph/tokens": "^0.1.3",
21
+ "@csstools/postcss-global-data": "^4.0.0",
22
+ "@telegraph/tokens": "^0.1.4",
23
23
  "autoprefixer": "^10.4.21",
24
24
  "postcss": "^8.5.6",
25
25
  "postcss-combine-duplicated-selectors": "^10.0.3",
@@ -30,7 +30,7 @@
30
30
  "@knocklabs/eslint-config": "^0.0.5",
31
31
  "@knocklabs/typescript-config": "^0.0.2",
32
32
  "@telegraph/prettier-config": "^0.0.7",
33
- "eslint": "^8.56.0",
33
+ "eslint": "^10.0.2",
34
34
  "typescript": "^5.9.3"
35
35
  }
36
36
  }