@scattered-light/loomiere 0.0.0-stage → 0.1.1

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.
Files changed (73) hide show
  1. package/README.md +60 -2
  2. package/bin/loomiere.mjs +112 -0
  3. package/build/build-tokens.mjs +485 -0
  4. package/exporters/README.md +45 -0
  5. package/exporters/ag-grid-settings.mjs +177 -0
  6. package/exporters/ag-grid.mjs +112 -0
  7. package/exporters/chakra.mjs +630 -0
  8. package/exporters/component-styles.mjs +207 -0
  9. package/exporters/dtcg.mjs +31 -0
  10. package/exporters/figma.mjs +100 -0
  11. package/exporters/index.mjs +38 -0
  12. package/exporters/mui.mjs +666 -0
  13. package/exporters/tailwind.mjs +35 -0
  14. package/exporters/theme.mjs +93 -0
  15. package/generator/brand.mjs +328 -0
  16. package/generator/brand.schema.json +657 -0
  17. package/generator/color.mjs +130 -0
  18. package/generator/data-colors.mjs +104 -0
  19. package/generator/format-brand.mjs +60 -0
  20. package/generator/generate.mjs +43 -0
  21. package/generator/pairs.mjs +236 -0
  22. package/generator/roles.mjs +258 -0
  23. package/generator/vision.mjs +167 -0
  24. package/lib/config.mjs +71 -0
  25. package/lib/contrast.mjs +134 -0
  26. package/lib/lab.mjs +108 -0
  27. package/lib/studio.mjs +127 -0
  28. package/lib/tool-server.mjs +96 -0
  29. package/package.json +51 -4
  30. package/templates/brand.json +104 -0
  31. package/tokens/README.md +219 -0
  32. package/tokens/component/avatar.json +10 -0
  33. package/tokens/component/button.json +12 -0
  34. package/tokens/component/drawer.json +26 -0
  35. package/tokens/component/navigation-menu.json +13 -0
  36. package/tokens/component/popover.json +13 -0
  37. package/tokens/component/preview-card.json +13 -0
  38. package/tokens/component/slider.json +13 -0
  39. package/tokens/component/switch.json +41 -0
  40. package/tokens/component/toast.json +21 -0
  41. package/tokens/component/toggle.json +9 -0
  42. package/tokens/component/toolbar.json +9 -0
  43. package/tokens/component/tooltip.json +9 -0
  44. package/tokens/foundation/layering.json +57 -0
  45. package/tokens/foundation/layout.json +75 -0
  46. package/tokens/foundation/motion.json +41 -0
  47. package/tokens/foundation/type.json +92 -0
  48. package/tokens/semantic/accessibility.json +39 -0
  49. package/tokens/semantic/bar.json +45 -0
  50. package/tokens/semantic/check.json +57 -0
  51. package/tokens/semantic/color.json +270 -0
  52. package/tokens/semantic/content.json +124 -0
  53. package/tokens/semantic/disclosure.json +49 -0
  54. package/tokens/semantic/elevation.json +72 -0
  55. package/tokens/semantic/field.json +77 -0
  56. package/tokens/semantic/focus.json +36 -0
  57. package/tokens/semantic/gauge.json +45 -0
  58. package/tokens/semantic/icon-button.json +33 -0
  59. package/tokens/semantic/icon.json +30 -0
  60. package/tokens/semantic/item.json +65 -0
  61. package/tokens/semantic/modal.json +86 -0
  62. package/tokens/semantic/motion.json +20 -0
  63. package/tokens/semantic/notice.json +37 -0
  64. package/tokens/semantic/popup.json +89 -0
  65. package/tokens/semantic/scheme.json +11 -0
  66. package/tokens/semantic/shape.json +54 -0
  67. package/tokens/semantic/spacing.json +110 -0
  68. package/tokens/semantic/table.json +73 -0
  69. package/tokens/semantic/typography.json +312 -0
  70. package/tools/brand-builder/builder.js +1119 -0
  71. package/tools/brand-builder/index.html +329 -0
  72. package/tools/pair-lab/index.html +119 -0
  73. package/tools/pair-lab/lab.js +249 -0
@@ -0,0 +1,45 @@
1
+ # Exporters
2
+
3
+ One theme, many frameworks. The universal outputs come first; everything else is a small
4
+ adapter in this folder, and each one only renames. No adapter does color math.
5
+
6
+ | Id | Output | For |
7
+ |---|---|---|
8
+ | (built in) | `tokens.css` | Any framework, or none. Every token as a `--sl-` variable, with dark, contrast, color vision and roles. |
9
+ | `dtcg` | `tokens.dtcg.json` | Any tool that reads the W3C design tokens format: Figma importers, Tokens Studio, Style Dictionary, Terrazzo, Penpot. |
10
+ | `tailwind` | `tailwind.css` | Tailwind CSS v4. Points at the `--sl-` variables instead of copying values. |
11
+ | `mui` | `mui.js`, `.d.ts` | MUI 6, 7 and 9. `createTheme(options)`: the palette in light and dark, plus component overrides that match loomiere-base-ui. Needs `tokens.css`. |
12
+ | `chakra` | `chakra.js`, `.d.ts` | Chakra UI 3. `<ChakraProvider value={system}>`: every palette as a color palette, plus recipes that match loomiere-base-ui. Needs `tokens.css`. |
13
+ | `ag-grid-v36` | `ag-grid-v36.js`, `.d.ts`, `.css` | AG Grid 36 and later (Theming API). |
14
+ | `ag-grid-v33` | `ag-grid-v33.js`, `.d.ts`, `.css` | AG Grid 33 to 35 (Theming API). |
15
+ | `ag-grid-v32` | `ag-grid-v32.css` | AG Grid 31 and 32 (CSS themes). All three come from `ag-grid-settings.mjs`. |
16
+ | `figma` | `figma.json` | Figma variables in three collections: Brand, Pairs, Theme (Light and Dark modes). Aliases point back to Brand, so a palette change flows through. Fluid sizes use their large-screen value. |
17
+
18
+ `loomiere build` writes them all to the theme's `exports/` folder, with `catalog.json` listing what's there.
19
+ `"exports": ["ag-grid", "tailwind"]` in `loomiere.config.json` writes only those (an id prefix picks a
20
+ family, so `ag-grid` writes all three AG Grid themes). The Mojave demo package exposes them as
21
+ `@scattered-light/loomiere-mojave/exports/<file>`.
22
+
23
+ ## How it works
24
+
25
+ 1. The build writes the token catalog (`tokens.json`).
26
+ 2. `theme.mjs` reads it back into a framework-neutral theme: every palette, every scope's slots
27
+ in light and dark, the semantic surfaces, text and borders in light and dark, fonts, sizes,
28
+ radii, space and shadows, plus the brand's roles.
29
+ 3. Each adapter maps that theme onto one framework's shape.
30
+
31
+ MUI and Chakra style their own components, so their adapters also restyle those to match
32
+ loomiere-base-ui. `component-styles.mjs` turns the shared patterns (field, popup surface, item
33
+ row, check box, focus ring, emphasis levels and so on) into style objects that read the same
34
+ concept variables the Base UI CSS reads; `mui.mjs` and `chakra.mjs` place them on each
35
+ library's own parts. A change to a semantic concept reaches all three.
36
+
37
+ ## Add a framework
38
+
39
+ Write `<id>.mjs` exporting `{ id, name, file, for, build(theme) }`, where `build` returns the
40
+ file's text (or `{ fileName: text }` when it writes several, as the AG Grid themes do), and add it to `EXPORTERS` in `index.mjs`. Prefer pointing at `--sl-` variables
41
+ when the framework allows it (as Tailwind does), so modes and roles keep working at runtime;
42
+ otherwise give light and dark values the way the framework expects (as MUI's palette does,
43
+ since MUI does color math on it).
44
+ Good next candidates: Panda CSS, Radix Themes, shadcn/ui (Tailwind plus its own variable
45
+ names), Vuetify and Mantine.
@@ -0,0 +1,177 @@
1
+ /**
2
+ * AG Grid settings table: one row per grid setting, pointing at the theme's semantic tokens.
3
+ * The three AG Grid themes (v32 CSS, v33 and v36 Theming API) are all written from this table,
4
+ * so a grid looks like the rest of the theme in every version, and light, dark and data-color
5
+ * follow the same tokens as the Base UI components.
6
+ *
7
+ * Each row is [setting, value, legacy variable]:
8
+ * setting the Theming API parameter name (AG Grid 33 and later).
9
+ * value "{token.path}" becomes var(--sl-token-path); text around the braces is kept, so
10
+ * "{stroke.default} solid {table.border-color}" is a CSS border. No braces = literal.
11
+ * legacy the --ag-* CSS variable that does the same job in v31-32 themes, or null when
12
+ * the old themes have no such variable. [variable, value] when the old variable
13
+ * takes a different value (a color where the new setting takes a whole border).
14
+ * npm run check:ag-grid confirms every setting and variable exists in the AG Grid version it
15
+ * is written for.
16
+ */
17
+
18
+ /** Settings added after 33; the v33 theme leaves them out. */
19
+ export const SINCE = {
20
+ fontWeight: 36,
21
+ borderWidth: 36,
22
+ cellFontWeight: 36,
23
+ focusErrorShadow: 36,
24
+ };
25
+
26
+ export const SETTINGS = [
27
+ // Color scheme: native scrollbars and pickers follow the theme in effect.
28
+ ['browserColorScheme', '{color-scheme}', null],
29
+
30
+ // Cells
31
+ ['backgroundColor', '{table.background}', '--ag-background-color'],
32
+ ['foregroundColor', '{table.color}', '--ag-foreground-color'],
33
+ ['textColor', '{table.color}', null],
34
+ ['cellTextColor', '{table.color}', '--ag-data-color'],
35
+ ['subtleTextColor', '{table.subtle-color}', '--ag-secondary-foreground-color'],
36
+ ['accentColor', '{interactive.default}', '--ag-active-color'],
37
+ ['invalidColor', '{field.border-color-error}', '--ag-invalid-color'],
38
+ ['oddRowBackgroundColor', '{table.background}', '--ag-odd-row-background-color'],
39
+ ['rowHoverColor', '{table.row-hover}', '--ag-row-hover-color'],
40
+ ['selectedRowBackgroundColor', '{table.row-selected}', '--ag-selected-row-background-color'],
41
+ ['columnHoverColor', '{table.column-hover}', '--ag-column-hover-color'],
42
+ ['rangeSelectionBackgroundColor', '{table.range-background}', '--ag-range-selection-background-color'],
43
+ ['rangeSelectionBorderColor', '{table.range-border}', '--ag-range-selection-border-color'],
44
+ ['cellEditingBorder', '{field.border-width} solid {field.border-color-focus}', null],
45
+ ['cellEditingShadow', '{field.shadow-focus}', null],
46
+
47
+ // Type: cells use the condense text style, the header uses condense-subtitle, whole.
48
+ ['fontFamily', '{font.condense.font-family}', '--ag-font-family'],
49
+ ['fontSize', '{font.condense.font-size}', '--ag-font-size'],
50
+ ['fontWeight', '{font.condense.font-weight}', null],
51
+ ['headerFontFamily', '{font.condense-subtitle.font-family}', null],
52
+ ['headerFontSize', '{font.condense-subtitle.font-size}', null],
53
+ ['headerFontWeight', '{font.condense-subtitle.font-weight}', null],
54
+
55
+ // Sizes
56
+ ['spacing', '{space.2}', '--ag-grid-size'],
57
+ ['rowHeight', '{table.row-height}', '--ag-row-height'],
58
+ ['headerHeight', '{table.header-height}', '--ag-header-height'],
59
+ ['listItemHeight', '{control.height-sm}', '--ag-list-item-height'],
60
+ ['cellHorizontalPadding', '{table.cell-padding-inline}', '--ag-cell-horizontal-padding'],
61
+ ['iconSize', '{icon-size.condense}', '--ag-icon-size'],
62
+
63
+ // Edges
64
+ ['borderColor', '{table.border-color}', '--ag-border-color'],
65
+ ['wrapperBorder', '{stroke.default} solid {table.border-color}', null],
66
+ ['wrapperBorderRadius', '{table.radius}', '--ag-wrapper-border-radius'],
67
+ ['borderRadius', '{radius.item}', '--ag-border-radius'],
68
+ ['borderWidth', '{stroke.default}', null],
69
+ ['rowBorder', '{stroke.default} solid {table.row-border-color}', ['--ag-row-border-color', '{table.row-border-color}']],
70
+ ['headerRowBorder', '{stroke.default} solid {table.border-color}', null],
71
+ ['columnBorder', 'none', null],
72
+ ['headerColumnResizeHandleColor', '{table.border-color}', '--ag-header-column-resize-handle-color'],
73
+
74
+ // Header, side bar and other chrome
75
+ ['chromeBackgroundColor', '{table.header-background}', '--ag-control-panel-background-color'],
76
+ ['headerBackgroundColor', '{table.header-background}', '--ag-header-background-color'],
77
+ ['headerTextColor', '{table.header-color}', '--ag-header-foreground-color'],
78
+ ['headerCellHoverBackgroundColor', '{table.header-background-hover}', null],
79
+ ['sideBarBackgroundColor', '{table.header-background}', null],
80
+ ['sideButtonSelectedBackgroundColor', '{table.background}', '--ag-side-button-selected-background-color'],
81
+ ['iconColor', '{table.subtle-color}', '--ag-icon-font-color'],
82
+
83
+ // Focus: the same ring and halo as every other control.
84
+ ['focusShadow', '0 0 0 {a11y.focus-outline-width} {focus.ring}, 0 0 0 {focus.halo-spread} {focus.halo-color}', null],
85
+ ['focusErrorShadow', '{field.shadow-focus-error}', null],
86
+
87
+ // Inputs (filters, cell editors, the quick filter): the field concept.
88
+ ['inputBackgroundColor', '{field.background}', null],
89
+ ['inputTextColor', '{field.color}', null],
90
+ ['inputPlaceholderTextColor', '{field.placeholder-color}', null],
91
+ ['inputBorder', '{field.border-width} solid {field.border-color}', ['--ag-input-border-color', '{field.border-color}']],
92
+ ['inputBorderRadius', '{field.radius}', null],
93
+ ['inputFocusBorder', '{field.border-width} solid {field.border-color-focus}', ['--ag-input-focus-border-color', '{field.border-color-focus}']],
94
+ ['inputFocusShadow', '{field.shadow-focus}', '--ag-input-focus-box-shadow'],
95
+ ['inputInvalidBorder', '{field.border-width} solid {field.border-color-error}', ['--ag-input-border-color-invalid', '{field.border-color-error}']],
96
+ ['inputDisabledBackgroundColor', '{field.background-disabled}', '--ag-input-disabled-background-color'],
97
+ ['inputDisabledTextColor', '{field.color-disabled}', null],
98
+ ['inputIconColor', '{table.subtle-color}', null],
99
+ ['inputHeight', '{control.height-sm}', null],
100
+ ['inputPaddingStart', '{control.padding-inline-sm}', null],
101
+
102
+ // Checkboxes and toggles: the check concept.
103
+ ['checkboxBorderRadius', '{check.radius}', null],
104
+ ['checkboxBorderWidth', '{check.border-width}', null],
105
+ ['checkboxUncheckedBackgroundColor', '{check.background}', '--ag-checkbox-background-color'],
106
+ ['checkboxUncheckedBorderColor', '{check.border-color}', '--ag-checkbox-unchecked-color'],
107
+ ['checkboxCheckedBackgroundColor', '{check.background-checked}', '--ag-checkbox-checked-color'],
108
+ ['checkboxCheckedBorderColor', '{check.border-color-checked}', null],
109
+ ['checkboxCheckedShapeColor', '{check.mark-color}', null],
110
+ ['checkboxIndeterminateBackgroundColor', '{check.background-checked}', '--ag-checkbox-indeterminate-color'],
111
+ ['checkboxIndeterminateBorderColor', '{check.border-color-checked}', null],
112
+ ['checkboxIndeterminateShapeColor', '{check.mark-color}', null],
113
+ ['toggleButtonOnBackgroundColor', '{check.background-checked}', null],
114
+ ['toggleButtonOffBackgroundColor', '{check.border-color}', null],
115
+ ['toggleButtonSwitchBackgroundColor', '{check.background}', null],
116
+
117
+ // Menus, pickers and panels: the popup and item concepts.
118
+ ['menuBackgroundColor', '{popup.background}', '--ag-menu-background-color'],
119
+ ['menuTextColor', '{item.color}', null],
120
+ ['menuBorder', '{popup.border-width} solid {popup.border-color}', ['--ag-menu-border-color', '{popup.border-color}']],
121
+ ['menuShadow', '{popup.shadow}', null],
122
+ ['menuSeparatorColor', '{popup.separator-color}', null],
123
+ ['popupShadow', '{popup.shadow}', '--ag-popup-shadow'],
124
+ ['dropdownShadow', '{popup.shadow}', null],
125
+ ['pickerListBackgroundColor', '{popup.background}', null],
126
+ ['pickerListBorder', '{popup.border-width} solid {popup.border-color}', null],
127
+ ['panelBackgroundColor', '{popup.background}', '--ag-panel-background-color'],
128
+ ['panelTitleBarBackgroundColor', '{table.header-background}', null],
129
+ ['cardShadow', '{elevation.sm}', '--ag-card-shadow'],
130
+ ['dialogBorder', '{modal.border-width} solid {modal.border-color}', null],
131
+ ['dialogShadow', '{modal.shadow}', null],
132
+ ['modalOverlayBackgroundColor', '{modal.backdrop}', '--ag-modal-overlay-background-color'],
133
+ ['tooltipBackgroundColor', '{surface.inverse}', '--ag-tooltip-background-color'],
134
+ ['tooltipTextColor', '{text.on-inverse-surface}', null],
135
+ ['tooltipBorder', 'none', null],
136
+
137
+ // Buttons (filter Apply/Reset, panels): the standard emphasis level, like an outline Button.
138
+ ['buttonBackgroundColor', '{emphasis.standard.bg}', null],
139
+ ['buttonTextColor', '{emphasis.standard.fg}', null],
140
+ ['buttonBorder', '{stroke.default} solid {emphasis.standard.border}', null],
141
+ ['buttonHoverBackgroundColor', '{emphasis.standard.bg-hover}', null],
142
+ ['buttonHoverBorder', '{stroke.default} solid {emphasis.standard.border-hover}', null],
143
+ ['buttonActiveBackgroundColor', '{emphasis.standard.bg-active}', null],
144
+ ['buttonBorderRadius', '{radius.control}', null],
145
+ ['buttonFontWeight', '{font.condense-trigger.font-weight}', null],
146
+ ['iconButtonColor', '{icon-button.color}', null],
147
+ ['iconButtonHoverColor', '{icon-button.color-hover}', null],
148
+ ['iconButtonHoverBackgroundColor', '{icon-button.background-hover}', null],
149
+ ['iconButtonBorderRadius', '{icon-button.radius}', null],
150
+
151
+ // Tabs (column menu, tool panels): the disclosure concept, like Base UI Tabs.
152
+ ['tabTextColor', '{table.subtle-color}', null],
153
+ ['tabHoverTextColor', '{table.color}', null],
154
+ ['tabSelectedTextColor', '{disclosure.selected-color}', null],
155
+ ['tabSelectedUnderlineColor', '{disclosure.indicator-color}', null],
156
+ ['tabSelectedUnderlineWidth', '{disclosure.indicator-width}', null],
157
+
158
+ // Value change flashes: the success and error palettes.
159
+ ['valueChangeDeltaUpColor', '{scope.success.fg}', null],
160
+ ['valueChangeDeltaDownColor', '{scope.error.fg}', null],
161
+ ];
162
+
163
+ /** Old-theme variables with no single Theming API setting of their own. */
164
+ export const LEGACY_ONLY = [
165
+ ['--ag-secondary-border-color', '{table.row-border-color}'],
166
+ ['--ag-subheader-background-color', '{table.header-background}'],
167
+ ['--ag-disabled-foreground-color', '{text.disabled}'],
168
+ ['--ag-input-disabled-border-color', '{field.border-color}'],
169
+ ['--ag-header-cell-hover-background-color', '{table.header-background-hover}'],
170
+ ['--ag-header-column-separator-display', 'none'],
171
+ ['--ag-borders', 'solid {stroke.default}'],
172
+ ['--ag-borders-critical', 'solid {stroke.default}'],
173
+ ['--ag-row-border-style', 'solid'],
174
+ ['--ag-row-border-width', '{stroke.default}'],
175
+ ['--ag-checkbox-border-radius', '{check.radius}'],
176
+ ['--ag-card-radius', '{radius.item}'],
177
+ ];
@@ -0,0 +1,112 @@
1
+ /**
2
+ * AG Grid themes, one per family of AG Grid versions, all written from ag-grid-settings.mjs.
3
+ * v36 (36 and later) Theming API, with the settings 36 added.
4
+ * v33 (33 to 35) Theming API.
5
+ * v32 (31 and 32) The old CSS themes: --ag-* variables on .ag-theme-quartz.
6
+ * Every value points at a --sl- variable, so light, dark, contrast and data-color keep working.
7
+ */
8
+ import { LEGACY_ONLY, SETTINGS, SINCE } from './ag-grid-settings.mjs';
9
+
10
+ /** Turns "{a.b} solid {c}" into "var(--sl-a-b) solid var(--sl-c)", failing on unknown tokens. */
11
+ function resolver(theme) {
12
+ const name = new Map(theme.tokens.map((t) => [t.path, t.name]));
13
+ return (value) =>
14
+ value.replace(/\{([^}]+)\}/g, (_, path) => {
15
+ if (!name.has(path)) throw new Error(`AG Grid settings: no token "${path}"`);
16
+ return `var(${name.get(path)})`;
17
+ });
18
+ }
19
+
20
+ /** Text-style details AG Grid has no settings for (letter spacing), plus per-version fixes. */
21
+ const extraCss = (fixes = '') => `/* Letter spacing comes from the condense and condense-subtitle text styles. */
22
+ .ag-root-wrapper,
23
+ .ag-popup {
24
+ letter-spacing: var(--sl-font-condense-letter-spacing);
25
+ }
26
+ .ag-header-cell-text {
27
+ letter-spacing: var(--sl-font-condense-subtitle-letter-spacing);
28
+ line-height: var(--sl-font-condense-subtitle-line-height);
29
+ }
30
+ ${fixes}`;
31
+
32
+ function themingApi(major, fixes) {
33
+ const file = `ag-grid-v${major}`;
34
+ return {
35
+ id: `ag-grid-v${major}`,
36
+ name: `AG Grid ${major === 33 ? '33 to 35' : `${major}+`}`,
37
+ files: [`${file}.js`, `${file}.d.ts`, `${file}.css`],
38
+ file: `${file}.js`,
39
+ for: `AG Grid ${major === 33 ? '33, 34 and 35' : '36 and later'} (Theming API): themeQuartz.withParams(brandParams), plus ${file}.css.`,
40
+ build(theme) {
41
+ const resolve = resolver(theme);
42
+ const rows = SETTINGS.filter(([param]) => !(SINCE[param] > major));
43
+ const body = rows.map(([param, value]) => ` ${param}: ${JSON.stringify(resolve(value))},`).join('\n');
44
+ const usage = ` * import { themeQuartz } from 'ag-grid-community';\n * import { withBrand } from './theme/exports/ag-grid-v${major}.js';\n * import './theme/tokens.css';\n * import './theme/exports/ag-grid-v${major}.css';\n * <AgGridReact theme={withBrand(themeQuartz)} ... />`;
45
+ return {
46
+ [`${file}.js`]: `/**\n * AG Grid ${major === 33 ? '33 to 35' : '36 and later'} theme for "${theme.brand}". Generated by loomiere build from exporters/ag-grid-settings.mjs; do not edit.\n${usage}\n * Every value is a --${theme.prefix}- variable, so light and dark follow data-theme or the system.\n */\nexport const brandParams = {\n${body}\n};\n\n/** Applies the brand's settings to an AG Grid theme, usually themeQuartz. */\nexport const withBrand = (base) => base.withParams(brandParams);\n`,
47
+ [`${file}.d.ts`]: `/** AG Grid ${major === 33 ? '33 to 35' : '36 and later'} theme settings for "${theme.brand}". */\nexport declare const brandParams: Readonly<Record<string, string>>;\n/** Applies the brand's settings to an AG Grid theme, usually themeQuartz. */\nexport declare function withBrand<T extends { withParams(params: object): T }>(base: T): T;\n`,
48
+ [`${file}.css`]: `/* AG Grid ${major === 33 ? '33 to 35' : '36 and later'} additions for "${theme.brand}". Generated by loomiere build; do not edit. Load after tokens.css. */\n${extraCss(fixes)}`,
49
+ };
50
+ },
51
+ };
52
+ }
53
+
54
+ export const agGridV36 = themingApi(36);
55
+
56
+ export const agGridV33 = themingApi(
57
+ 33,
58
+ `/* 33 to 35 hard-code a 5px radius on picker fields (36 uses borderRadius). */
59
+ .ag-picker-field-wrapper {
60
+ border-radius: var(--sl-field-radius);
61
+ }
62
+
63
+ /* 33 to 35 give focused text inputs the keyboard focus ring; 36 uses inputFocusShadow, like Input. */
64
+ .ag-root-wrapper .ag-input-field-input:focus,
65
+ .ag-popup .ag-input-field-input:focus {
66
+ box-shadow: var(--sl-field-shadow-focus);
67
+ }
68
+ `,
69
+ );
70
+
71
+ export const agGridV32 = {
72
+ id: 'ag-grid-v32',
73
+ name: 'AG Grid 31 and 32',
74
+ files: ['ag-grid-v32.css'],
75
+ file: 'ag-grid-v32.css',
76
+ for: 'AG Grid 31 and 32 (CSS themes): load after ag-theme-quartz.css and put class="ag-theme-quartz ag-theme-loomiere" on the grid.',
77
+ build(theme) {
78
+ const resolve = resolver(theme);
79
+ const lines = [];
80
+ for (const [, value, legacy] of SETTINGS) {
81
+ if (!legacy) continue;
82
+ const [variable, legacyValue] = Array.isArray(legacy) ? legacy : [legacy, value];
83
+ lines.push(` ${variable}: ${resolve(legacyValue)};`);
84
+ }
85
+ for (const [variable, value] of LEGACY_ONLY) lines.push(` ${variable}: ${resolve(value)};`);
86
+ return {
87
+ 'ag-grid-v32.css': `/* AG Grid 31 and 32 theme for "${theme.brand}". Generated by loomiere build from exporters/ag-grid-settings.mjs; do not edit.
88
+ import 'ag-grid-community/styles/ag-grid.css';
89
+ import 'ag-grid-community/styles/ag-theme-quartz.css';
90
+ import './theme/tokens.css';
91
+ import './theme/exports/ag-grid-v32.css';
92
+ <div class="ag-theme-quartz ag-theme-loomiere">…grid…</div>
93
+ Every value is a --${theme.prefix}- variable, so light and dark follow data-theme or the system;
94
+ no -dark theme class is needed. */
95
+ .ag-theme-quartz.ag-theme-loomiere {
96
+ ${lines.join('\n')}
97
+ color-scheme: var(--${theme.prefix}-color-scheme);
98
+ }
99
+
100
+ /* The old themes have no header type settings: the header uses the condense-subtitle text style. */
101
+ .ag-theme-loomiere .ag-header-cell-text {
102
+ font-family: var(--${theme.prefix}-font-condense-subtitle-font-family);
103
+ font-size: var(--${theme.prefix}-font-condense-subtitle-font-size);
104
+ font-weight: var(--${theme.prefix}-font-condense-subtitle-font-weight);
105
+ }
106
+ .ag-theme-loomiere .ag-header-cell:hover {
107
+ background-color: var(--${theme.prefix}-table-header-background-hover);
108
+ }
109
+ ${extraCss().replace(/^\.ag-root-wrapper,\n\.ag-popup \{/m, '.ag-theme-loomiere {').replace(/^\.ag-header-cell-text \{/m, '.ag-theme-loomiere .ag-header-cell-text {')}`,
110
+ };
111
+ },
112
+ };