Skip to content

Commit e695579

Browse files
authored
feat: Disable inline style prop for components
Added a custom ESLint rule `gamut/no-inline-style` to `eslint-plugin-gamut` to disallow inline `style` props on JSX elements. This rule encourages the use of styled components, CSS classes, or design system utilities instead of inline styles. **Changes:** - Created `no-inline-style` rule with full test coverage in `eslint-plugin-gamut` - Updated `.eslintrc.js` to use the custom rule (set to 'error' level) - Added rule to `recommended` configuration - Disabled rule for `gamut-illustrations` package where inline styles are acceptable - Added ESLint disable comments to existing components that require inline styles (Card, Rotation, ExpandInCollapseOut, FocusTrap, Popover, VidstackPlayer, SelectDropdown components) - Created comprehensive ESLint rules documentation page in Storybook (`Meta/ESLint Rules`) - Added ESLint rules link to Meta section table of contents
1 parent 4805bb0 commit e695579

File tree

19 files changed

+390
-12
lines changed

19 files changed

+390
-12
lines changed

.eslintrc.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module.exports = {
1111
rules: {
1212
'gamut/prefer-themed': 'error',
1313
'gamut/no-css-standalone': 'error',
14+
'gamut/no-inline-style': 'error',
1415
'gamut/import-paths': 'error',
1516
'import/no-extraneous-dependencies': 'off',
1617
},
@@ -66,5 +67,11 @@ module.exports = {
6667
'lodash/import-scope': ['error', 'method'],
6768
},
6869
},
70+
{
71+
files: ['packages/gamut-illustrations/**'],
72+
rules: {
73+
'gamut/no-inline-style': 'off',
74+
},
75+
},
6976
],
7077
};

packages/eslint-plugin-gamut/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module.exports = {
1616
'gamut/prefer-themed': 'error',
1717
'gamut/no-css-standalone': 'error',
1818
'gamut/import-paths': 'error',
19+
'gamut/no-inline-style': 'error',
1920
},
2021
};
2122
```

packages/eslint-plugin-gamut/src/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import gamutImportPaths from './gamut-import-paths';
22
import noCssStandalone from './no-css-standalone';
3+
import noInlineStyle from './no-inline-style';
34
import preferThemed from './prefer-themed';
45
import recommended from './recommended';
56

67
const rules = {
78
'import-paths': gamutImportPaths,
89
'no-css-standalone': noCssStandalone,
10+
'no-inline-style': noInlineStyle,
911
'prefer-themed': preferThemed,
1012
};
1113

packages/eslint-plugin-gamut/src/no-css-standalone.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default createRule({
1919
},
2020
messages: {
2121
noCssStandalone:
22-
"Do not create anymore stylesheets, please see Gamut's Notion page for current best practices.",
22+
"Do not create anymore stylesheets, please see Gamut's Storybook documentation for current best practices.",
2323
},
2424
type: 'suggestion',
2525
schema: [],
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { ESLintUtils } from '@typescript-eslint/utils';
2+
3+
import rule from './no-inline-style';
4+
5+
const ruleTester = new ESLintUtils.RuleTester({
6+
parser: '@typescript-eslint/parser',
7+
parserOptions: {
8+
ecmaFeatures: {
9+
jsx: true,
10+
},
11+
},
12+
});
13+
14+
ruleTester.run('no-inline-style', rule, {
15+
valid: [
16+
`<div />`,
17+
`<div className="foo" />`,
18+
`<Component className="bar" />`,
19+
`<div styles={styles} />`,
20+
`<Component styleSheet={sheet} />`,
21+
`const style = { padding: 0 };`,
22+
],
23+
invalid: [
24+
{
25+
code: `<div style={{ padding: 0 }} />`,
26+
errors: [
27+
{
28+
messageId: 'noInlineStyle',
29+
},
30+
],
31+
},
32+
{
33+
code: `<Component style={{ margin: '10px' }} />`,
34+
errors: [
35+
{
36+
messageId: 'noInlineStyle',
37+
},
38+
],
39+
},
40+
{
41+
code: `<button style={styles.button} />`,
42+
errors: [
43+
{
44+
messageId: 'noInlineStyle',
45+
},
46+
],
47+
},
48+
{
49+
code: `<div className="foo" style={{ color: 'red' }} />`,
50+
errors: [
51+
{
52+
messageId: 'noInlineStyle',
53+
},
54+
],
55+
},
56+
{
57+
code: `
58+
<Component
59+
style={{
60+
padding: 0,
61+
margin: 0,
62+
}}
63+
/>
64+
`,
65+
errors: [
66+
{
67+
messageId: 'noInlineStyle',
68+
},
69+
],
70+
},
71+
],
72+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { createRule } from './createRule';
2+
3+
export default createRule({
4+
create(context) {
5+
return {
6+
JSXAttribute(node) {
7+
if (node.name.type === 'JSXIdentifier' && node.name.name === 'style') {
8+
context.report({
9+
messageId: 'noInlineStyle',
10+
node,
11+
});
12+
}
13+
},
14+
};
15+
},
16+
defaultOptions: [],
17+
meta: {
18+
docs: {
19+
description: 'Disallow inline style props on JSX elements.',
20+
recommended: 'error',
21+
},
22+
messages: {
23+
noInlineStyle:
24+
'The use of inline styles is discouraged — consider using styled components, design system utilities, or CSS classes instead.',
25+
},
26+
type: 'suggestion',
27+
schema: [],
28+
},
29+
name: 'no-inline-style',
30+
});

packages/eslint-plugin-gamut/src/recommended.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export default {
22
rules: {
33
'gamut/no-css-standalone': 'error',
4+
'gamut/no-inline-style': 'error',
45
'gamut/prefer-themed': 'off',
56
'gamut/gamut-import-paths': 'error',
67
},

packages/gamut/src/Animation/ExpandInCollapseOut.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const ExpandInCollapseOut: React.FC<WithChildrenProp> = ({
1111
animate="expanded"
1212
exit="collapsed"
1313
initial="collapsed"
14+
// eslint-disable-next-line gamut/no-inline-style
1415
style={{ overflow: 'hidden' }}
1516
transition={{ duration: timingValues.medium / 1000, ease: 'easeInOut' }}
1617
variants={{

packages/gamut/src/Animation/Rotation.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export const Rotation: React.FC<RotationProps> = ({
3535
children,
3636
...rest
3737
}) => (
38+
/* eslint-disable gamut/no-inline-style */
3839
<motion.div
3940
animate={rotated ? 'rotated' : 'normal'}
4041
style={{
@@ -52,4 +53,5 @@ export const Rotation: React.FC<RotationProps> = ({
5253
>
5354
{children}
5455
</motion.div>
56+
/* eslint-enable gamut/no-inline-style */
5557
);

packages/gamut/src/Card/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export const Card: React.FC<CardProps> = ({
4545
left={shadow === 'patternLeft' ? '-0.5rem' : undefined}
4646
position="absolute"
4747
right={shadow === 'patternRight' ? '-0.5rem' : undefined}
48+
// eslint-disable-next-line gamut/no-inline-style
4849
style={{ borderRadius: 'inherit', color: 'currentcolor' }}
4950
top=".5rem"
5051
/>

0 commit comments

Comments
 (0)