@turk.net/mui 3.0.7 → 3.0.8

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.
@@ -0,0 +1,271 @@
1
+ # OneHub Design Tokens Map
2
+
3
+ > **Purpose:** Complete mapping of Figma design tokens to MUI theme paths and CSS custom properties.
4
+ >
5
+ > **Critical Rule:** ALL colors MUST be referenced via CSS custom properties (preferred) or `theme.vars.palette.*` paths. NEVER use raw hex values or RGB values directly in component code.
6
+
7
+ ---
8
+
9
+ ## Color System Architecture
10
+
11
+ OneHub uses a **3-tier color architecture**:
12
+
13
+ ```
14
+ Layer 1: Raw color tokens (colors.ts) → gray-2, primary-10, red-origin, ...
15
+ Layer 2: Semantic palettes (palettes.ts) → neutral.background[1], brand.foreground[2], ...
16
+ Layer 3: CSS custom props (colorSchemes.ts) → --neutral-background-1-rest, --brand-foreground-2-rest, ...
17
+ ```
18
+
19
+ ### State Suffixes
20
+ Every color token has **4 interaction states**:
21
+
22
+ | State | Suffix | Usage |
23
+ |-------|--------|-------|
24
+ | **rest** | `-rest` | Default/normal state |
25
+ | **hover** | `-hover` | Mouse hover |
26
+ | **pressed** | `-pressed` | Active/clicked |
27
+ | **selected** | `-selected` | Currently selected |
28
+
29
+ ---
30
+
31
+ ## Palette Categories
32
+
33
+ ### 1. Neutral Palette (`neutral`)
34
+
35
+ Default UI elements: backgrounds, text, borders.
36
+
37
+ #### Background (`neutral.background`)
38
+
39
+ | Figma CSS Var | MUI Theme Path | Typical Color (Light) | Usage |
40
+ |---------------|---------------|----------------------|-------|
41
+ | `--neutral-background-1-rest` | `theme.vars.palette.neutral.background[1].rest` | `#ffffff` (white) | Primary surface: page bg, card bg, table row |
42
+ | `--neutral-background-2-rest` | `theme.vars.palette.neutral.background[2].rest` | `#fafafa` | Secondary surface: table header, sidebar bg |
43
+ | `--neutral-background-3-rest` | `theme.vars.palette.neutral.background[3].rest` | — | Tertiary surface |
44
+ | `--neutral-background-4-rest` | `theme.vars.palette.neutral.background[4].rest` | — | Quaternary surface |
45
+ | `--neutral-background-5-rest` | `theme.vars.palette.neutral.background[5].rest` | — | 5th level surface |
46
+ | `--neutral-background-6-rest` | `theme.vars.palette.neutral.background[6].rest` | — | 6th level surface |
47
+ | `--neutral-background-disabled-rest` | `theme.vars.palette.neutral.background.disabled.rest` | — | Disabled element bg |
48
+ | `--neutral-background-inverted-rest` | `theme.vars.palette.neutral.background.inverted.rest` | `#000000` | Inverted background (dark bg on light) |
49
+ | `--neutral-background-overlay-rest` | `theme.vars.palette.neutral.background.overlay.rest` | `rgba(0,0,0,0.40)` | Modal/dialog overlay |
50
+ | `--neutral-background-static-rest` | `theme.vars.palette.neutral.background.static.rest` | — | Static background (doesn't invert) |
51
+ | `--neutral-background-stencil-rest` | `theme.vars.palette.neutral.background.stencil.rest` | — | Skeleton/placeholder bg |
52
+ | `--neutral-background-subtle-rest` | `theme.vars.palette.neutral.background.subtle.rest` | `rgba(255,255,255,0)` | Transparent bg (icon buttons) |
53
+
54
+ #### Foreground / Text (`neutral.foreground`)
55
+
56
+ | Figma CSS Var | MUI Theme Path | Typical Color (Light) | Usage |
57
+ |---------------|---------------|----------------------|-------|
58
+ | `--neutral-foreground-1-rest` | `theme.vars.palette.neutral.foreground[1].rest` | `#242424` | Primary text |
59
+ | `--neutral-foreground-2-rest` | `theme.vars.palette.neutral.foreground[2].rest` | `#424242` | Secondary text, form labels |
60
+ | `--neutral-foreground-3-rest` | `theme.vars.palette.neutral.foreground[3].rest` | — | Tertiary text |
61
+ | `--neutral-foreground-4-rest` | `theme.vars.palette.neutral.foreground[4].rest` | `#707070` | Placeholder, disabled text |
62
+ | `--neutral-foreground-disabled-rest` | `theme.vars.palette.neutral.foreground.disabled.rest` | — | Fully disabled text |
63
+ | `--neutral-foreground-on-brand-rest` | `theme.vars.palette.neutral.foreground.on.brand.rest` | `#ffffff` | Text on brand-colored bg |
64
+
65
+ #### Stroke / Border (`neutral.stroke`)
66
+
67
+ | Figma CSS Var | MUI Theme Path | Typical Color (Light) | Usage |
68
+ |---------------|---------------|----------------------|-------|
69
+ | `--neutral-stroke-1-rest` | `theme.vars.palette.neutral.stroke[1].rest` | `#d1d1d1` | Primary border (inputs, outlined buttons) |
70
+ | `--neutral-stroke-2-rest` | `theme.vars.palette.neutral.stroke[2].rest` | — | Secondary border |
71
+ | `--neutral-stroke-3-rest` | `theme.vars.palette.neutral.stroke[3].rest` | `#f0f0f0` | Subtle divider (table borders) |
72
+ | `--neutral-stroke-accessible-rest` | `theme.vars.palette.neutral.stroke.accessible.rest` | `#616161` | Accessible border (checkboxes, focus rings) |
73
+ | `--neutral-stroke-disabled-rest` | `theme.vars.palette.neutral.stroke.disabled.rest` | — | Disabled element border |
74
+ | `--neutral-stroke-focus-rest` | `theme.vars.palette.neutral.stroke.focus.rest` | — | Focus ring color |
75
+ | `--neutral-stroke-subtle-rest` | `theme.vars.palette.neutral.stroke.subtle.rest` | — | Very subtle border |
76
+
77
+ ---
78
+
79
+ ### 2. Brand Palette (`brand`)
80
+
81
+ Primary brand color (blue). Used for primary actions, selected states, brand elements.
82
+
83
+ | Figma CSS Var | MUI Theme Path | Typical Color (Light) | Usage |
84
+ |---------------|---------------|----------------------|-------|
85
+ | `--brand-background-1-rest` | `theme.vars.palette.brand.background[1].rest` | `#2970ff` | Primary button bg, selected state |
86
+ | `--brand-background-2-rest` | `theme.vars.palette.brand.background[2].rest` | `#e7efff` | Chip/Badge bg, filter tag bg |
87
+ | `--brand-background-compound-rest` | `theme.vars.palette.brand.background.compound.rest` | `#2970ff` | Compound bg (checkbox fill, radio fill) |
88
+ | `--brand-foreground-1-rest` | `theme.vars.palette.brand.foreground[1].rest` | — | Brand-colored text |
89
+ | `--brand-foreground-2-rest` | `theme.vars.palette.brand.foreground[2].rest` | `#2362df` | Chip/Badge text on brand bg |
90
+ | `--brand-foreground-compound-rest` | `theme.vars.palette.brand.foreground.compound.rest` | — | Compound foreground |
91
+ | `--brand-stroke-1-rest` | `theme.vars.palette.brand.stroke[1].rest` | — | Brand border |
92
+ | `--brand-stroke-2-rest` | `theme.vars.palette.brand.stroke[2].rest` | `#b7cfff` | Chip/Badge border |
93
+ | `--brand-stroke-compound-rest` | `theme.vars.palette.brand.stroke.compound.rest` | — | Compound stroke |
94
+
95
+ ---
96
+
97
+ ### 3. Danger Palette (`danger`)
98
+
99
+ Error, destructive actions, critical states. Uses red tones.
100
+
101
+ | Figma CSS Var | MUI Theme Path | Usage |
102
+ |---------------|---------------|-------|
103
+ | `--danger-background-1-rest` | `theme.vars.palette.danger.background[1].rest` | Error button bg |
104
+ | `--danger-background-2-rest` | `theme.vars.palette.danger.background[2].rest` | Error chip/badge bg |
105
+ | `--danger-foreground-1-rest` | `theme.vars.palette.danger.foreground[1].rest` | Error text |
106
+ | `--danger-foreground-2-rest` | `theme.vars.palette.danger.foreground[2].rest` | Error chip/badge text |
107
+ | `--danger-stroke-1-rest` | `theme.vars.palette.danger.stroke[1].rest` | Error border |
108
+
109
+ ---
110
+
111
+ ### 4. Shared Palette (`shared`)
112
+
113
+ Common semantic colors available as sub-palettes.
114
+
115
+ | Sub-palette | Typical Use |
116
+ |-------------|-------------|
117
+ | `shared.red` | Error, destructive |
118
+ | `shared.green` | Success, positive |
119
+ | `shared.orange` | Severe warning |
120
+ | `shared.yellow` | Warning |
121
+ | `shared.purple` | Informational, special |
122
+ | `shared.pink` | Accent |
123
+ | `shared.cyan` | Info, link |
124
+ | `shared.lime` | Success alternative |
125
+
126
+ Each has `background`, `foreground`, and `stroke` properties with 4 states.
127
+
128
+ ---
129
+
130
+ ## Typography Token Map
131
+
132
+ ### Untitled UI System → MUI Variant → Figma Equivalent
133
+
134
+ OneHub uses custom typography variants. MUI default `h1`-`h6`, `body1`, `body2`, `subtitle1`, `subtitle2`, `caption`, `button` are **DISABLED** and MUST NOT be used.
135
+
136
+ #### Display Scale
137
+
138
+ | MUI Variant | Font Size | Line Height | Font Weight(s) | Figma Class Equivalent |
139
+ |-------------|-----------|-------------|----------------|----------------------|
140
+ | `display.xl2.bold` | 72px | 90px | 700 | — |
141
+ | `display.xl.bold` | 60px | 72px | 700 | — |
142
+ | `display.lg.bold` | 48px | 60px | 700 | — |
143
+ | `display.lg.semibold` | 48px | 60px | 600 | — |
144
+ | `display.md.bold` | 36px | 38px | 700 | — |
145
+ | `display.md.semibold` | 36px | 38px | 600 | — |
146
+ | `display.sm.bold` | 30px | 38px | 700 | — |
147
+ | `display.xs.bold` | 24px | 32px | 700 | — |
148
+ | `display.xs.semibold` | 24px | 32px | 600 | — |
149
+
150
+ #### Text Scale
151
+
152
+ | MUI Variant | Font Size | Line Height | Font Weight(s) | Figma Equivalent |
153
+ |-------------|-----------|-------------|----------------|-----------------|
154
+ | `text.xl.medium` | 20px | 30px | 500 | `text-[20px] leading-[30px] font-medium` |
155
+ | `text.xl.regular` | 20px | 30px | 400 | `text-[20px] leading-[30px] font-normal` |
156
+ | `text.lg.medium` | 18px | 28px | 500 | `text-[18px] leading-[28px] font-medium` |
157
+ | `text.lg.regular` | 18px | 28px | 400 | `text-[18px] leading-[28px] font-normal` |
158
+ | `text.md.medium` | 16px | 28px | 500 | `text-[16px] leading-[28px] font-medium` |
159
+ | `text.md.regular` | 16px | 28px | 400 | `text-[16px] leading-[28px] font-normal` |
160
+ | `text.sm.semibold` | 14px | 20px | 600 | `text-[14px] leading-[20px] font-semibold` |
161
+ | `text.sm.medium` | 14px | 20px | 500 | `text-[14px] leading-[20px] font-medium` |
162
+ | `text.sm.regular` | 14px | 20px | 400 | `text-[14px] leading-[20px] font-normal` |
163
+ | `text.xs.medium` | 12px | 18px | 500 | `text-[12px] leading-[18px] font-medium` |
164
+ | `text.xs.regular` | 12px | 18px | 400 | `text-[12px] leading-[18px] font-normal` |
165
+
166
+ ### Usage Rules
167
+
168
+ - **Page titles** MUST use `display.*` variants
169
+ - **Section headers** MUST use `text.xl.*` or `text.lg.*`
170
+ - **Body text** MUST use `text.md.*` or `text.sm.*`
171
+ - **Table headers** MUST use `text.xs.medium`
172
+ - **Labels, captions** MUST use `text.xs.*`
173
+ - **NEVER use `variant="h1"`, `variant="body1"`, etc.** — these are disabled
174
+
175
+ ---
176
+
177
+ ## Spacing System
178
+
179
+ ### Base Grid: 4px
180
+
181
+ MU theme uses **`spacing: 4`** instead of MUI default 8. All spacing values are multiples of 4.
182
+
183
+ | Figma Value | MUI equivalent | Usage Pattern |
184
+ |-------------|---------------|---------------|
185
+ | 4px | `theme.spacing(1)` | Icon gaps, tight padding |
186
+ | 8px | `theme.spacing(2)` | Standard gap between elements |
187
+ | 12px | `theme.spacing(3)` | Button horizontal padding |
188
+ | 16px | `theme.spacing(4)` | Card padding, section gap |
189
+ | 24px | `theme.spacing(6)` | Large section gap |
190
+ | 32px | `theme.spacing(8)` | Table cell horizontal padding |
191
+ | 40px | `theme.spacing(10)` | — |
192
+ | 48px | `theme.spacing(12)` | — |
193
+ | 64px | `theme.spacing(16)` | Page padding |
194
+
195
+ ### Common Figma Spacing Patterns
196
+
197
+ | Figma Pattern | MUI Code Equivalent |
198
+ |---------------|-------------------|
199
+ | `gap-[8px]` | `gap: theme.spacing(2)` or `sx={{ gap: 2 }}` |
200
+ | `gap-[4px]` | `gap: theme.spacing(1)` or `sx={{ gap: 1 }}` |
201
+ | `px-[32px]` (table cell) | `paddingLeft: theme.spacing(8), paddingRight: theme.spacing(8)` |
202
+ | `py-[16px]` (section) | `paddingTop: theme.spacing(4), paddingBottom: theme.spacing(4)` |
203
+ | `px-[8px]` (button icon) | `paddingLeft: theme.spacing(2), paddingRight: theme.spacing(2)` |
204
+ | `px-[12px]` (button text) | `paddingLeft: theme.spacing(3), paddingRight: theme.spacing(3)` |
205
+
206
+ ### Magic Number Rule
207
+
208
+ > **MUST NOT use raw pixel values.** Every spacing value MUST pass through `theme.spacing(n)`.
209
+
210
+ ```typescript
211
+ // ✅ CORRECT
212
+ sx={{ padding: theme.spacing(2) }}
213
+
214
+ // ❌ WRONG — Magic numbers
215
+ sx={{ padding: '8px' }}
216
+ sx={{ padding: 8 }}
217
+ ```
218
+
219
+ ---
220
+
221
+ ## Border Radius
222
+
223
+ | Figma Value | MUI Equivalent | Usage |
224
+ |-------------|---------------|-------|
225
+ | `rounded-[4px]` | `theme.shape.borderRadius / 2` (4px) | Checkbox, small elements |
226
+ | `rounded-[8px]` | `theme.shape.borderRadius` (8px) | Buttons, inputs, cards, radio buttons |
227
+ | `rounded-[16px]` | `theme.shape.borderRadius * 2` (16px) | Chips, badges, filter tags |
228
+
229
+ ---
230
+
231
+ ## Shadow System
232
+
233
+ 25-level shadow system. Access via `theme.shadows[n]`.
234
+
235
+ | Level | Figma Pattern | Usage |
236
+ |-------|--------------|-------|
237
+ | `shadows[0]` | `none` | Flat elements |
238
+ | `shadows[1]` | — | MUI default (minimal shadow) |
239
+ | `shadows[2]` | `0 0 2px rgba(0,0,0,0.12)` | Very subtle elevation |
240
+ | `shadows[3]` | `0 0 4px rgba(0,0,0,0.12)` | Slight elevation |
241
+ | `shadows[4]` | `0 0 8px rgba(0,0,0,0.12)` | Dropdowns, tooltips |
242
+ | `shadows[5]` | `0 0 16px rgba(0,0,0,0.12)` | Dialogs |
243
+ | `shadows[6]` | `0 0 28px rgba(0,0,0,0.12)` | Modals |
244
+ | `shadows[7]` | `0 0 64px rgba(0,0,0,0.12)` | High elevation modals |
245
+ | `shadows[8-13]` | Brand shadows (`rgba(0,0,0,0.3)` overlay) | Branded elevated elements |
246
+ | `shadows[14-24]` | MUI defaults | — |
247
+
248
+ ---
249
+
250
+ ## Sizing System
251
+
252
+ ### Component Height Table (from Figma)
253
+
254
+ | Component | Height | Notes |
255
+ |-----------|--------|-------|
256
+ | Button (medium) | `32px` | Standard action button |
257
+ | Table Header | `40px` | — |
258
+ | Table Row | `56px` | — |
259
+ | Filter Button | `32px` | Same as button |
260
+ | Checkbox/Radio | `16px` | Square (width = height) |
261
+
262
+ ---
263
+
264
+ ## Font Family
265
+
266
+ | Property | Value |
267
+ |----------|-------|
268
+ | **Family** | Inter (`var(--font-inter)`) |
269
+ | **Weights** | 400 (Regular), 500 (Medium), 600 (Semibold), 700 (Bold) |
270
+ | **Host Project Responsibility** | MUST define `@font-face` and set `--font-inter` CSS variable |
271
+ | **Package** | Font files NOT distributed by `@turknet/onehub`. Host project must self-host. |
@@ -0,0 +1,392 @@
1
+ # ESLint Design Rules — Specification
2
+
3
+ > **Purpose:** Specification for a custom ESLint plugin that automates OneHub design rule enforcement.
4
+ >
5
+ > **Status:** Specification / future implementation. Currently enforced via `scripts/verify-design.js`.
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ This document defines ESLint rules that can automatically detect design violations in OneHub projects. Rules are divided into:
12
+
13
+ 1. **Auto-detectable** — Can be reliably detected by static analysis (ESLint)
14
+ 2. **Manual review required** — Needs human/AI judgment, cannot be fully automated
15
+
16
+ ---
17
+
18
+ ## Auto-Detectable Rules (ESLint)
19
+
20
+ ### `onehub/no-mui-default-typography`
21
+
22
+ Detects usage of MUI default typography variants that are disabled in the theme.
23
+
24
+ **Rule:** Disallow `variant="h1"` through `variant="h6"`, `variant="body1"`, `variant="body2"`, `variant="subtitle1"`, `variant="subtitle2"`, `variant="caption"`, `variant="button"`, `variant="overline"`.
25
+
26
+ ```json
27
+ {
28
+ "onehub/no-mui-default-typography": "error"
29
+ }
30
+ ```
31
+
32
+ **Detects:**
33
+ ```tsx
34
+ // ❌ Error
35
+ <Typography variant="h1">Title</Typography>
36
+ <Typography variant="body1">Text</Typography>
37
+ <Typography variant="caption">Caption</Typography>
38
+
39
+ // ✅ OK
40
+ <Typography variant="display.lg.bold">Title</Typography>
41
+ <Typography variant="text.md.regular">Text</Typography>
42
+ ```
43
+
44
+ ---
45
+
46
+ ### `onehub/no-inline-style`
47
+
48
+ Detects inline `style={{}}` props on JSX elements.
49
+
50
+ **Rule:** Disallow `style={{}` prop usage. Must use `sx` or `styled()`.
51
+
52
+ ```json
53
+ {
54
+ "onehub/no-inline-style": "error"
55
+ }
56
+ ```
57
+
58
+ **Detects:**
59
+ ```tsx
60
+ // ❌ Error
61
+ <Button style={{ color: 'red' }}>Click</Button>
62
+ <div style={{ padding: 20 }}>Text</div>
63
+
64
+ // ✅ OK
65
+ <Button sx={{ color: theme.vars.palette.danger.foreground[1].rest }}>
66
+ <Box sx={{ padding: theme.spacing(5) }}>
67
+ ```
68
+
69
+ ---
70
+
71
+ ### `onehub/no-raw-hex-colors`
72
+
73
+ Detects raw hex color values in JSX attributes and template literals.
74
+
75
+ **Rule:** Disallow `#`-prefixed hex colors in `sx`, `css`, `styled` (unless inside `theme.vars.*` or `var(--*)` context).
76
+
77
+ ```json
78
+ {
79
+ "onehub/no-raw-hex-colors": "error"
80
+ }
81
+ ```
82
+
83
+ **Detects:**
84
+ ```tsx
85
+ // ❌ Error
86
+ sx={{ color: '#242424' }}
87
+ sx={{ backgroundColor: '#2970ff' }}
88
+
89
+ // ✅ OK
90
+ sx={{ color: 'var(--neutral-foreground-1-rest)' }}
91
+ sx={{ bgcolor: theme.vars.palette.brand.background[1].rest }}
92
+ ```
93
+
94
+ **Edge cases to handle:**
95
+ - SVG fill values may legitimately use hex colors → exclude SVG elements
96
+ - Storybook stories may use hex for demo purposes → allow in `.stories.tsx` files
97
+
98
+ ---
99
+
100
+ ### `onehub/no-magic-spacing`
101
+
102
+ Detects raw pixel values and numeric spacing that should use `theme.spacing()`.
103
+
104
+ **Rule:** Disallow string px values (`'8px'`) and raw number spacing values in `sx` and `css` props.
105
+
106
+ ```json
107
+ {
108
+ "onehub/no-magic-spacing": ["error", { "allowedZero": true }]
109
+ }
110
+ ```
111
+
112
+ **Detects:**
113
+ ```tsx
114
+ // ❌ Error
115
+ sx={{ padding: '8px' }}
116
+ sx={{ margin: '16px' }}
117
+ sx={{ gap: '4px' }}
118
+
119
+ // ✅ OK
120
+ sx={{ padding: theme.spacing(2) }}
121
+ sx={{ margin: theme.spacing(4) }}
122
+ sx={{ gap: theme.spacing(1) }}
123
+ sx={{ padding: 0 }}
124
+ ```
125
+
126
+ ---
127
+
128
+ ### `onehub/no-invalid-mui-color-prop`
129
+
130
+ Detects MUI `color` prop values that are not valid for the specific component.
131
+
132
+ **Rule:** Enforce valid `color` prop values per component based on theme definitions.
133
+
134
+ ```json
135
+ {
136
+ "onehub/no-invalid-mui-color-prop": "error"
137
+ }
138
+ ```
139
+
140
+ **Detects:**
141
+ ```tsx
142
+ // ❌ Error
143
+ <Button color="success">Save</Button> // Valid: primary, secondary, error
144
+ <TextField color="primary" /> // Valid: neutral, brand, danger
145
+ <Chip color="success" /> // Valid: primary, secondary, error
146
+ <Badge color="primary" /> // Valid: default, brand, success, danger, ...warning, important
147
+
148
+ // ✅ OK
149
+ <Button color="primary" />
150
+ <TextField color="neutral" />
151
+ <Chip color="primary" />
152
+ <Badge color="success" />
153
+ ```
154
+
155
+ ---
156
+
157
+ ### `onehub/no-invalid-mui-variant-prop`
158
+
159
+ Detects MUI `variant` prop values that are not valid for the specific component.
160
+
161
+ ```json
162
+ {
163
+ "onehub/no-invalid-mui-variant-prop": "error"
164
+ }
165
+ ```
166
+
167
+ **Detects:**
168
+ ```tsx
169
+ // ❌ Error
170
+ <Button variant="gradient">Click</Button> // Valid: contained, outlined, text
171
+ <Chip variant="soft" /> // Valid: filled, tint, outline, ghost
172
+
173
+ // ✅ OK
174
+ <Button variant="contained" />
175
+ <Chip variant="tint" />
176
+ ```
177
+
178
+ ---
179
+
180
+ ### `onehub/no-full-mui-import`
181
+
182
+ Detects full library imports from `@mui/material`.
183
+
184
+ ```json
185
+ {
186
+ "onehub/no-full-mui-import": "error"
187
+ }
188
+ ```
189
+
190
+ **Detects:**
191
+ ```tsx
192
+ // ❌ Error
193
+ import * as Mui from '@mui/material';
194
+
195
+ // ✅ OK
196
+ import { Button, Typography, Box } from '@mui/material';
197
+ ```
198
+
199
+ ---
200
+
201
+ ### `onehub/no-checkbox-radio-size-large`
202
+
203
+ Detects `size="large"` on Checkbox and Radio components.
204
+
205
+ ```json
206
+ {
207
+ "onehub/no-checkbox-radio-size-large": "error"
208
+ }
209
+ ```
210
+
211
+ **Detects:**
212
+ ```tsx
213
+ // ❌ Error
214
+ <Checkbox size="large" />
215
+ <Radio size="large" />
216
+
217
+ // ✅ OK
218
+ <Checkbox size="small" />
219
+ <Radio size="medium" />
220
+ ```
221
+
222
+ ---
223
+
224
+ ### `onehub/no-non-vars-palette`
225
+
226
+ Detects usage of `theme.palette.*` (non-vars palette) instead of `theme.vars.palette.*`.
227
+
228
+ ```json
229
+ {
230
+ "onehub/no-non-vars-palette": "error"
231
+ }
232
+ ```
233
+
234
+ **Detects:**
235
+ ```tsx
236
+ // ❌ Error
237
+ sx={{ color: theme.palette.primary.main }}
238
+ sx={{ bgcolor: theme.palette.error.light }}
239
+
240
+ // ✅ OK
241
+ sx={{ color: theme.vars.palette.brand.foreground[1].rest }}
242
+ sx={{ bgcolor: theme.vars.palette.danger.background[2].rest }}
243
+ ```
244
+
245
+ ---
246
+
247
+ ## Manual Review Required (Cannot be fully automated)
248
+
249
+ These rules require human or AI judgment because context matters.
250
+
251
+ | Rule | Why Manual |
252
+ |------|-----------|
253
+ | Typography variant matches content hierarchy | Page title vs section header — depends on intent |
254
+ | Correct MUI component selected for Figma pattern | Requires visual comparison |
255
+ | Form uses Formik + Yup | Can check imports but can't verify correct usage |
256
+ | Data fetching uses React Query | Can check imports but can't verify correct pattern |
257
+ | Icons from correct source | `@turknet/onehub/icons` vs `@mui/icons-material` can be checked by import source |
258
+ | Accessibility (labels, keyboard nav) | Needs behavioral/visual verification |
259
+ | Border radius matches element type | Context-dependent |
260
+ | Shadow/elevation level appropriate | Context-dependent |
261
+ | Layout component used correctly | Needs understanding of page structure |
262
+
263
+ ---
264
+
265
+ ## Configuration Format
266
+
267
+ ### `.eslintrc.json` Example
268
+
269
+ ```json
270
+ {
271
+ "plugins": ["onehub"],
272
+ "rules": {
273
+ "onehub/no-mui-default-typography": "error",
274
+ "onehub/no-inline-style": "error",
275
+ "onehub/no-raw-hex-colors": "error",
276
+ "onehub/no-magic-spacing": "error",
277
+ "onehub/no-invalid-mui-color-prop": "error",
278
+ "onehub/no-invalid-mui-variant-prop": "error",
279
+ "onehub/no-full-mui-import": "error",
280
+ "onehub/no-checkbox-radio-size-large": "error",
281
+ "onehub/no-non-vars-palette": "error"
282
+ }
283
+ }
284
+ ```
285
+
286
+ ### `eslint.config.mjs` (Flat Config) Example
287
+
288
+ ```js
289
+ import onehub from 'eslint-plugin-onehub';
290
+
291
+ export default [
292
+ {
293
+ plugins: { onehub },
294
+ rules: {
295
+ 'onehub/no-mui-default-typography': 'error',
296
+ 'onehub/no-inline-style': 'error',
297
+ 'onehub/no-raw-hex-colors': 'error',
298
+ 'onehub/no-magic-spacing': 'error',
299
+ 'onehub/no-invalid-mui-color-prop': 'error',
300
+ 'onehub/no-invalid-mui-variant-prop': 'error',
301
+
302
+ 'onehub/no-full-mui-import': 'error',
303
+ 'onehub/no-checkbox-radio-size-large': 'error',
304
+ 'onehub/no-non-vars-palette': 'error',
305
+ },
306
+ },
307
+ ];
308
+ ```
309
+
310
+ ---
311
+
312
+ ## Integration Steps
313
+
314
+ ### 1. Install the ESLint plugin
315
+
316
+ ```bash
317
+ yarn add -D eslint-plugin-onehub
318
+ # or
319
+ npm install --save-dev eslint-plugin-onehub
320
+ ```
321
+
322
+ ### 2. Add to ESLint config
323
+
324
+ Add `"onehub"` to the `plugins` array and configure rules as shown above.
325
+
326
+ ### 3. Add lint script
327
+
328
+ ```json
329
+ {
330
+ "scripts": {
331
+ "lint:design": "eslint --rule 'onehub/*: error' src/"
332
+ }
333
+ }
334
+ ```
335
+
336
+ ### 4. CI Integration
337
+
338
+ ```yaml
339
+ # bitbucket-pipelines.yml example
340
+ design-lint:
341
+ - step:
342
+ name: Design Compliance Check
343
+ script:
344
+ - yarn lint:design
345
+ - node node_modules/@turknet/onehub/scripts/verify-design.js src/
346
+ ```
347
+
348
+ ---
349
+
350
+ ## Rule Priority & Adoption Order
351
+
352
+ When implementing the ESLint plugin, prioritize rules in this order:
353
+
354
+ | Priority | Rule | Reason |
355
+ |----------|------|--------|
356
+ | **P0** | `no-mui-default-typography` | High impact, easy to detect |
357
+ | **P0** | `no-inline-style` | Common antipattern |
358
+ | **P1** | `no-raw-hex-colors` | High impact, some edge cases |
359
+ | **P1** | `no-magic-spacing` | High impact, some edge cases |
360
+ | **P2** | `no-invalid-mui-color-prop` | Per-component validation |
361
+ | **P2** | `no-invalid-mui-variant-prop` | Per-component validation |
362
+ | **P2** | `no-full-mui-import` | Bundle size |
363
+ | **P3** | `no-checkbox-radio-size-large` | Edge case |
364
+ | **P3** | `no-non-vars-palette` | Edge case, may have legit uses |
365
+
366
+ ---
367
+
368
+ ## Current Enforcement (Until ESLint Plugin Ready)
369
+
370
+ Until the custom ESLint plugin is implemented, use:
371
+
372
+ 1. **`scripts/verify-design.js`** — Runs in CI and pre-commit
373
+ ```bash
374
+ node scripts/verify-design.js src/
375
+ ```
376
+
377
+ 2. **`.opencode/rules/design-compliance.md`** — Always-apply rule for AI agents
378
+
379
+ 3. **`.opencode/skills/onehub-design-verification/SKILL.md`** — Manual verification checklist
380
+
381
+ 4. **Manual code review** — Check against DESIGN_RULES.md
382
+
383
+ ---
384
+
385
+ ## References
386
+
387
+ - [ESLint Plugin Development Guide](https://eslint.org/docs/latest/extend/plugins)
388
+ - [ESTree AST Spec](https://github.com/estree/estree) — For writing AST-based rules
389
+ - [@typescript-eslint](https://typescript-eslint.io/) — TypeScript-aware linting
390
+ - `DESIGN_RULES.md` — Complete design rule reference
391
+ - `COMPONENT_MAP.md` — Valid prop combinations for all components
392
+ - `COMPONENT_GLOSSARY.md` — Exhaustive component catalog