@hirobius/design-system 0.20.0 → 0.22.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/AGENTS.md +125 -0
- package/CHANGELOG.md +969 -0
- package/CONSUMING.md +27 -11
- package/DESIGN.md +28 -17
- package/MIGRATIONS.md +603 -0
- package/NOTICE.md +1 -1
- package/README.md +39 -5
- package/UPGRADING.md +222 -0
- package/codemods/hds-prefix.mjs +28 -13
- package/codemods/jsx-fold.mjs +28 -13
- package/codemods/lib/atomic-write.mjs +28 -0
- package/codemods/lib/installed-version.mjs +354 -0
- package/codemods/lib/project.mjs +395 -0
- package/codemods/lib/record.mjs +110 -0
- package/codemods/lib/scan.mjs +323 -0
- package/codemods/lib/semver.mjs +57 -0
- package/codemods/not-found-pattern.mjs +3 -3
- package/codemods/patterns-subpath.mjs +28 -12
- package/codemods/registry.mjs +67 -0
- package/codemods/tile-grid.mjs +3 -3
- package/codemods/upgrade.mjs +909 -0
- package/dist/chunks/empty-state-DNoThgfV.js +1227 -0
- package/dist/chunks/form-By82qZcC.js +66 -0
- package/dist/fonts/geist-mono-400.woff2 +0 -0
- package/dist/fonts/satoshi-400.woff2 +0 -0
- package/dist/fonts/satoshi-500.woff2 +0 -0
- package/dist/fonts/satoshi-700.woff2 +0 -0
- package/dist/fonts.css +53 -0
- package/dist/form.js +1 -1
- package/dist/hirobius-ui.js +876 -877
- package/dist/manifest.js +3 -3
- package/dist/patterns.js +189 -170
- package/dist/styles.css +2 -2
- package/dist/tokens.css +1 -1
- package/dist/tokens.js +136 -57
- package/dist/types/src/app/components/alert-dialog.d.ts +5 -0
- package/dist/types/src/app/components/alert.d.ts +1 -1
- package/dist/types/src/app/components/asset-img.d.ts +1 -0
- package/dist/types/src/app/components/avatar-group.d.ts +1 -0
- package/dist/types/src/app/components/avatar.d.ts +1 -1
- package/dist/types/src/app/components/badge.d.ts +2 -2
- package/dist/types/src/app/components/blockquote.d.ts +1 -0
- package/dist/types/src/app/components/breadcrumb.d.ts +1 -1
- package/dist/types/src/app/components/button.d.ts +3 -1
- package/dist/types/src/app/components/callout.d.ts +1 -1
- package/dist/types/src/app/components/card.d.ts +4 -3
- package/dist/types/src/app/components/checkbox.d.ts +1 -1
- package/dist/types/src/app/components/code-block.d.ts +1 -1
- package/dist/types/src/app/components/combobox.d.ts +1 -1
- package/dist/types/src/app/components/data-table-section.d.ts +1 -0
- package/dist/types/src/app/components/destructive-section.d.ts +1 -0
- package/dist/types/src/app/components/dialog.d.ts +2 -1
- package/dist/types/src/app/components/disclosure.d.ts +1 -1
- package/dist/types/src/app/components/divider.d.ts +1 -1
- package/dist/types/src/app/components/empty-state.d.ts +1 -1
- package/dist/types/src/app/components/error-pattern.d.ts +6 -1
- package/dist/types/src/app/components/field.d.ts +1 -1
- package/dist/types/src/app/components/form-actions.d.ts +1 -0
- package/dist/types/src/app/components/form-control.d.ts +10 -0
- package/dist/types/src/app/components/form.d.ts +1 -1
- package/dist/types/src/app/components/grid.d.ts +5 -0
- package/dist/types/src/app/components/hds-tooltip.d.ts +1 -1
- package/dist/types/src/app/components/icon.d.ts +1 -0
- package/dist/types/src/app/components/inline-link.d.ts +1 -1
- package/dist/types/src/app/components/input.d.ts +1 -1
- package/dist/types/src/app/components/kbd.d.ts +1 -0
- package/dist/types/src/app/components/menu.d.ts +1 -1
- package/dist/types/src/app/components/metadata-list.d.ts +1 -0
- package/dist/types/src/app/components/metric-tiles.d.ts +1 -0
- package/dist/types/src/app/components/pagination.d.ts +1 -1
- package/dist/types/src/app/components/popover.d.ts +1 -1
- package/dist/types/src/app/components/progress.d.ts +2 -2
- package/dist/types/src/app/components/radio.d.ts +1 -1
- package/dist/types/src/app/components/select.d.ts +1 -1
- package/dist/types/src/app/components/skeleton.d.ts +2 -2
- package/dist/types/src/app/components/slider.d.ts +1 -1
- package/dist/types/src/app/components/spinner.d.ts +1 -1
- package/dist/types/src/app/components/stat.d.ts +5 -4
- package/dist/types/src/app/components/status-list-item.d.ts +1 -1
- package/dist/types/src/app/components/status-tile.d.ts +4 -3
- package/dist/types/src/app/components/table.d.ts +6 -1
- package/dist/types/src/app/components/tabs.d.ts +1 -1
- package/dist/types/src/app/components/tag.d.ts +1 -1
- package/dist/types/src/app/components/text.d.ts +53 -6
- package/dist/types/src/app/components/textarea.d.ts +1 -1
- package/dist/types/src/app/components/timestamp.d.ts +1 -0
- package/dist/types/src/app/components/toast.d.ts +1 -1
- package/dist/types/src/app/components/toggle.d.ts +1 -1
- package/dist/types/src/app/data/manifest-types.d.ts +4 -0
- package/dist/types/src/app/design-system/generated-token-refs.d.ts +37 -28
- package/dist/types/src/app/design-system/generated-token-values.d.ts +3 -0
- package/dist/types/src/app/design-system/tokens.d.ts +117 -15
- package/dist/types/src/index.d.ts +122 -72
- package/dist/variables.css +41 -33
- package/docs/CONSUMING.md +63 -39
- package/hirobius.tokens.json +163 -112
- package/llms.txt +158 -50
- package/mcp/catalog.mjs +369 -0
- package/mcp/core-set.mjs +66 -0
- package/mcp/guide.mjs +272 -0
- package/mcp/hds-mcp.mjs +49 -0
- package/mcp/server.mjs +94 -0
- package/package.json +53 -11
- package/public/hds-manifest.json +430 -336
- package/public/llms/components.txt +4 -3
- package/public/llms/layout.txt +1 -1
- package/public/llms-full.txt +190 -70
- package/public/llms.txt +158 -50
- package/scripts/eslint-plugin-hds/index.d.mts +29 -0
- package/scripts/eslint-plugin-hds/index.mjs +55 -0
- package/scripts/eslint-plugin-hds/package.json +30 -0
- package/scripts/eslint-plugin-hds/rules/no-raw-controls.mjs +47 -0
- package/scripts/eslint-plugin-hds/rules/no-raw-hex.mjs +70 -0
- package/scripts/eslint-plugin-hds/rules/no-raw-px-spacing.mjs +125 -0
- package/scripts/eslint-plugin-hds/rules/prefer-hds-layout-primitive.mjs +54 -0
- package/scripts/eslint-plugin-hds/rules/sx-token-first.mjs +111 -0
- package/scripts/eslint-plugin-hds/rules/utils.mjs +66 -0
- package/src/app/data/component-api.json +154 -93
- package/upgrade/index.json +208 -0
- package/upgrade/releases/0.17.0.json +962 -0
- package/upgrade/releases/0.18.0.json +8 -0
- package/upgrade/releases/0.19.0.json +233 -0
- package/upgrade/releases/0.19.1.json +8 -0
- package/upgrade/releases/0.20.0.json +5108 -0
- package/upgrade/releases/0.21.0.json +538 -0
- package/upgrade/releases/0.22.0.json +94 -0
- package/upgrade/schema.json +989 -0
- package/dist/chunks/empty-state-BBqhCx0S.js +0 -1217
- package/dist/chunks/form-COP3vK9_.js +0 -66
- package/dist/types/src/app/components/status-dot.d.ts +0 -39
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hds/prefer-hds-layout-primitive
|
|
3
|
+
*
|
|
4
|
+
* Warns (never errors) on ad-hoc `display: 'flex' | 'grid'` inside an
|
|
5
|
+
* inline `style={{ ... }}` object, where a named HDS layout primitive
|
|
6
|
+
* (Stack for flex, Grid for grid — see src/app/components/box.tsx's
|
|
7
|
+
* "reach for Stack/Grid/Container first" guidance) likely fits better than
|
|
8
|
+
* a one-off flex/grid container. Intentionally a warning: ad-hoc flex is
|
|
9
|
+
* sometimes the right call (e.g. deeply nested one-off alignment), so this
|
|
10
|
+
* nudges rather than blocks.
|
|
11
|
+
*/
|
|
12
|
+
import { attrName, jsxObjectExpression, propName, stringLiteralValue } from './utils.mjs';
|
|
13
|
+
|
|
14
|
+
const FLEX_GRID_RE = /^(?:inline-)?(flex|grid)$/;
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
meta: {
|
|
18
|
+
type: 'suggestion',
|
|
19
|
+
docs: {
|
|
20
|
+
description:
|
|
21
|
+
'Warn on ad-hoc display:flex/grid in inline style where a named HDS layout primitive (Stack/Grid) likely fits.',
|
|
22
|
+
recommended: true,
|
|
23
|
+
url: 'https://github.com/hirobius/hds/blob/main/scripts/eslint-plugin-hds/README.md#hdsprefer-hds-layout-primitive',
|
|
24
|
+
},
|
|
25
|
+
schema: [],
|
|
26
|
+
messages: {
|
|
27
|
+
preferPrimitive:
|
|
28
|
+
'Ad-hoc "display: {{display}}" in inline style — consider the named HDS layout primitive (Stack for flex, Grid for grid) instead of a one-off flex/grid container.',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
create(context) {
|
|
32
|
+
return {
|
|
33
|
+
JSXAttribute(node) {
|
|
34
|
+
if (attrName(node) !== 'style') return;
|
|
35
|
+
const obj = jsxObjectExpression(node.value);
|
|
36
|
+
if (!obj) return;
|
|
37
|
+
|
|
38
|
+
const displayProp = obj.properties.find(
|
|
39
|
+
(p) => p.type === 'Property' && propName(p.key) === 'display',
|
|
40
|
+
);
|
|
41
|
+
if (!displayProp) return;
|
|
42
|
+
|
|
43
|
+
const value = stringLiteralValue(displayProp.value);
|
|
44
|
+
if (value != null && FLEX_GRID_RE.test(value)) {
|
|
45
|
+
context.report({
|
|
46
|
+
node: displayProp.value,
|
|
47
|
+
messageId: 'preferPrimitive',
|
|
48
|
+
data: { display: value },
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hds/sx-token-first
|
|
3
|
+
*
|
|
4
|
+
* Errors on raw hex colors or raw px strings inside a `sx={{ ... }}` prop
|
|
5
|
+
* (Box's token-first layout engine — src/app/components/box.tsx). `sx` is
|
|
6
|
+
* the sanctioned escape hatch specifically because it forces spacing/color
|
|
7
|
+
* through HDS tokens. The spacing shorthands (`p`, `m`, `gap`, ...) take the
|
|
8
|
+
* t-shirt scale by name ('xs' | 'sm' | 'md' | 'lg' | 'xl', hds#206), and the
|
|
9
|
+
* fix this rule offers on them points there. No other key resolves those names
|
|
10
|
+
* (`width: 'md'` is invalid CSS), so a px string anywhere else is pointed at a
|
|
11
|
+
* token (`hds.space.*` or a `var(--...)`). Bare numbers still resolve off the
|
|
12
|
+
* 4px space scale (`p: 4` is 16px), so this rule does NOT flag them — only
|
|
13
|
+
* values that prove the author reached past the token system (hex strings,
|
|
14
|
+
* explicit 'Npx' strings). Nested objects (responsive `{ xs, sm, ... }` and
|
|
15
|
+
* `&`-selector blocks) are walked recursively since sx supports both; a value
|
|
16
|
+
* in a responsive map is reported against the key the map sits on.
|
|
17
|
+
*/
|
|
18
|
+
import { attrName, jsxObjectExpression, propName, stringLiteralValue } from './utils.mjs';
|
|
19
|
+
|
|
20
|
+
const HEX_RE = /#[0-9a-fA-F]{3,4}\b|#[0-9a-fA-F]{6}\b|#[0-9a-fA-F]{8}\b/;
|
|
21
|
+
const PX_STRING_RE = /-?\d+(?:\.\d+)?px/;
|
|
22
|
+
|
|
23
|
+
// The Box sx keys whose values go through the spacing resolver, the only
|
|
24
|
+
// keys that take the t-shirt names (box-sx.ts SPACING_PROP_MAP).
|
|
25
|
+
const SPACING_KEYS = new Set([
|
|
26
|
+
'm',
|
|
27
|
+
'mt',
|
|
28
|
+
'mr',
|
|
29
|
+
'mb',
|
|
30
|
+
'ml',
|
|
31
|
+
'mx',
|
|
32
|
+
'my',
|
|
33
|
+
'p',
|
|
34
|
+
'pt',
|
|
35
|
+
'pr',
|
|
36
|
+
'pb',
|
|
37
|
+
'pl',
|
|
38
|
+
'px',
|
|
39
|
+
'py',
|
|
40
|
+
'gap',
|
|
41
|
+
'rowGap',
|
|
42
|
+
'columnGap',
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
export default {
|
|
46
|
+
meta: {
|
|
47
|
+
type: 'problem',
|
|
48
|
+
docs: {
|
|
49
|
+
description:
|
|
50
|
+
'Disallow raw hex/px string values inside Box sx — sx must resolve colors and spacing through HDS token keys.',
|
|
51
|
+
recommended: true,
|
|
52
|
+
url: 'https://github.com/hirobius/hds/blob/main/scripts/eslint-plugin-hds/README.md#hdssx-token-first',
|
|
53
|
+
},
|
|
54
|
+
schema: [],
|
|
55
|
+
messages: {
|
|
56
|
+
rawHexInSx:
|
|
57
|
+
'Raw hex color "{{value}}" in sx prop "{{prop}}" bypasses HDS color tokens. Use a dotted token key (e.g. "content.primary", "surface.raised", "accent").',
|
|
58
|
+
rawPxInSx:
|
|
59
|
+
'Raw px string "{{value}}" in sx prop "{{prop}}" bypasses the HDS spacing scale. Use a t-shirt step: "xs" | "sm" | "md" | "lg" | "xl" (8/16/24/32/48px at comfortable density; compact tightens them).',
|
|
60
|
+
rawPxInSxNonSpacing:
|
|
61
|
+
'Raw px string "{{value}}" in sx prop "{{prop}}" bypasses HDS tokens. Use a token: an hds.space.* value or a var(--...) such as "var(--primitive-space-4)". Spacing step names apply to the spacing props (p, m, gap, ...) only.',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
create(context) {
|
|
65
|
+
/**
|
|
66
|
+
* @param objExpr an sx object, an `&`-selector block, or a responsive map
|
|
67
|
+
* @param mapKey the key a responsive map sits on (null for an sx object or
|
|
68
|
+
* an `&` block, whose keys are real sx keys)
|
|
69
|
+
*/
|
|
70
|
+
function walk(objExpr, mapKey = null) {
|
|
71
|
+
for (const prop of objExpr.properties) {
|
|
72
|
+
if (prop.type !== 'Property') continue; // skip SpreadElement
|
|
73
|
+
const name = propName(prop.key) ?? '?';
|
|
74
|
+
|
|
75
|
+
if (prop.value.type === 'ObjectExpression') {
|
|
76
|
+
// '&'-selector nesting holds sx keys again; anything else is a
|
|
77
|
+
// responsive { xs, sm, ... } map on this key.
|
|
78
|
+
walk(prop.value, name.startsWith('&') ? null : (mapKey ?? name));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const value = stringLiteralValue(prop.value);
|
|
83
|
+
if (value == null) continue;
|
|
84
|
+
const key = mapKey ?? name;
|
|
85
|
+
|
|
86
|
+
if (HEX_RE.test(value)) {
|
|
87
|
+
context.report({
|
|
88
|
+
node: prop.value,
|
|
89
|
+
messageId: 'rawHexInSx',
|
|
90
|
+
data: { value, prop: key },
|
|
91
|
+
});
|
|
92
|
+
} else if (PX_STRING_RE.test(value)) {
|
|
93
|
+
context.report({
|
|
94
|
+
node: prop.value,
|
|
95
|
+
messageId: SPACING_KEYS.has(key) ? 'rawPxInSx' : 'rawPxInSxNonSpacing',
|
|
96
|
+
data: { value, prop: key },
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
JSXAttribute(node) {
|
|
104
|
+
if (attrName(node) !== 'sx') return;
|
|
105
|
+
const obj = jsxObjectExpression(node.value);
|
|
106
|
+
if (!obj) return;
|
|
107
|
+
walk(obj);
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared AST helpers for the HDS ESLint rules. Deliberately AST-based (not
|
|
3
|
+
* regex-over-source-text like the repo's internal scripts/check-*.mjs
|
|
4
|
+
* guardrails) because this plugin runs inside consumers' own ESLint configs
|
|
5
|
+
* against arbitrary TSX, where a plain parser is already available and gives
|
|
6
|
+
* us exact node types for free.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Resolve a JSXAttribute's name, e.g. `style` / `className` / `sx`. */
|
|
10
|
+
export function attrName(node) {
|
|
11
|
+
return node.name && node.name.type === 'JSXIdentifier' ? node.name.name : null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Resolve an ObjectExpression Property key to a plain string, or null. */
|
|
15
|
+
export function propName(key) {
|
|
16
|
+
if (!key) return null;
|
|
17
|
+
if (key.type === 'Identifier') return key.name;
|
|
18
|
+
if (key.type === 'Literal' && typeof key.value === 'string') return key.value;
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolve a JSXAttribute's `{ ... }` expression to the ObjectExpression it
|
|
24
|
+
* wraps, e.g. `style={{ margin: 0 }}` -> the `{ margin: 0 }` ObjectExpression.
|
|
25
|
+
* Returns null for anything else (identifiers, spreads-only, conditional
|
|
26
|
+
* expressions, etc.) — this plugin only understands literal object props.
|
|
27
|
+
*/
|
|
28
|
+
export function jsxObjectExpression(attrValueNode) {
|
|
29
|
+
if (!attrValueNode || attrValueNode.type !== 'JSXExpressionContainer') return null;
|
|
30
|
+
const expr = attrValueNode.expression;
|
|
31
|
+
return expr && expr.type === 'ObjectExpression' ? expr : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a plain string literal value from a node — covers `'x'`/`"x"` and
|
|
36
|
+
* zero-interpolation template literals (`` `x` ``). Returns null for
|
|
37
|
+
* anything computed (identifiers, member expressions, interpolated
|
|
38
|
+
* templates) — this plugin only flags values it can prove are raw.
|
|
39
|
+
*/
|
|
40
|
+
export function stringLiteralValue(node) {
|
|
41
|
+
if (!node) return null;
|
|
42
|
+
if (node.type === 'Literal' && typeof node.value === 'string') return node.value;
|
|
43
|
+
if (node.type === 'TemplateLiteral' && node.expressions.length === 0) {
|
|
44
|
+
return node.quasis.map((q) => q.value.cooked ?? '').join('');
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resolve a numeric literal value from a node, including unary-minus
|
|
51
|
+
* (`-8`), which the estree parser represents as UnaryExpression, not a
|
|
52
|
+
* negative Literal. Returns null for anything else.
|
|
53
|
+
*/
|
|
54
|
+
export function numberLiteralValue(node) {
|
|
55
|
+
if (!node) return null;
|
|
56
|
+
if (node.type === 'Literal' && typeof node.value === 'number') return node.value;
|
|
57
|
+
if (
|
|
58
|
+
node.type === 'UnaryExpression' &&
|
|
59
|
+
node.operator === '-' &&
|
|
60
|
+
node.argument.type === 'Literal' &&
|
|
61
|
+
typeof node.argument.value === 'number'
|
|
62
|
+
) {
|
|
63
|
+
return -node.argument.value;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|