ESLint Plugin
ESLint plugin for Nord Design System - enforces logical CSS selectors, assists with migration from legacy classes, and integrates Tailwind best practices.
The @nordhealth/eslint-plugin helps you:
- Enforce logical CSS properties for better RTL/LTR support
- Migrate from legacy CSS with auto-fixable rules
- Follow Tailwind best practices with integrated rules from better-tailwindcss
Installation
npm install @nordhealth/eslint-plugin --save-devUsage
The plugin provides several preset configurations. Add one to your eslint.config.js:
import nordPlugin from "@nordhealth/eslint-plugin"
export default [
// Recommended: Nord rules + Tailwind best practices
nordPlugin.configs.recommended,
// For Vue projects (includes Vue parser)
nordPlugin.configs.vue,
]Available Configs
| Config | Description | Included Rules |
|---|---|---|
recommended | Nord + Tailwind rules (warnings) | @nordhealth/logical-selectors, @nordhealth/no-unknown-legacy-classes, better-tailwindcss rules |
recommended-error | Same as above but as errors | @nordhealth/logical-selectors, @nordhealth/no-unknown-legacy-classes, better-tailwindcss rules |
nord | Nord rules only (warnings) | @nordhealth/logical-selectors, @nordhealth/no-unknown-legacy-classes |
nord-error | Nord rules only (errors) | @nordhealth/logical-selectors, @nordhealth/no-unknown-legacy-classes |
vue | Recommended + Vue parser | @nordhealth/logical-selectors, @nordhealth/no-unknown-legacy-classes, better-tailwindcss rules, Vue parser |
vue-error | Vue config with errors | @nordhealth/logical-selectors, @nordhealth/no-unknown-legacy-classes, better-tailwindcss rules, Vue parser |
Nord Rules
@nordhealth/logical-selectorsRecommended
Enforces logical CSS properties over physical ones for better RTL/LTR support. Auto-fixable.
<!-- Bad -->
<div class="n:pl-m n:mr-l n:border-l">
<!-- Good -->
<div class="n:p-is-m n:m-ie-l n:border-is">Converts physical directions to logical equivalents:
| Physical | Logical | Description |
|---|---|---|
pl/pr | p-is/p-ie | Padding inline-start/end |
ml/mr | m-is/m-ie | Margin inline-start/end |
pt/pb | p-bs/p-be | Padding block-start/end |
mt/mb | m-bs/m-be | Margin block-start/end |
left/right | inset-is/inset-ie | Position inline-start/end |
top/bottom | inset-bs/inset-be | Position block-start/end |
border-l/border-r | border-is/border-ie | Border inline-start/end |
rounded-tl | rounded-ss | Border radius start-start |
rounded-tr | rounded-se | Border radius start-end |
rounded-bl | rounded-es | Border radius end-start |
rounded-br | rounded-ee | Border radius end-end |
@nordhealth/no-legacy-classesRecommended
Migrates deprecated Nord CSS classes (n-*) to their Tailwind equivalents (n:*). Auto-fixable.
<!-- Bad -->
<div class="n-padding-m n-margin-l n-color-text-weak">
<!-- Good (auto-fixed) -->
<div class="n:p-m n:m-l n:text-weak">Available categories:
| Category | Description |
|---|---|
spacing | Margin, padding, gap classes |
borders | Border width, color, radius classes |
typography | Font size, weight, family, line-height |
colors | Text, background, status colors |
layout | Flex, grid, alignment, container |
components | Form elements, icons |
utilities | Shadows, z-index, transitions, misc |
Configuration examples:
// Enable only specific categories
"@nordhealth/no-legacy-classes": ["warn", {
categories: ["spacing", "colors"]
}]
// Disable specific categories (enable all others)
"@nordhealth/no-legacy-classes": ["warn", {
disabledCategories: ["components"]
}]@nordhealth/no-unknown-legacy-classesRecommended
Flags n-* classes that aren't part of the Nord CSS Framework. This rule helps you:
- Catch typos in class names (e.g.,
n-maring-minstead ofn-margin-m) - Identify deprecated classes that have been removed from the framework
- Find custom classes that may need to be migrated or replaced
<!-- These will be flagged -->
<div class="n-maring-m"> <!-- typo: should be n-margin-m -->
<div class="n-padding-medium"> <!-- invalid: should be n-padding-m -->
<div class="n-custom-class"> <!-- unknown: not a Nord class -->
<!-- These are valid Nord classes -->
<div class="n-margin-m n-padding-l n-color-text">Disable this rule if needed:
rules: {
"@nordhealth/no-unknown-legacy-classes": "off"
}Tailwind Rules
The plugin includes the recommended rules from eslint-plugin-better-tailwindcss:
- Consistent class ordering - Ensures classes are in a consistent order
- No duplicate classes - Prevents accidentally duplicating classes
- No conflicting classes - Catches classes that conflict with each other
- Shorthand enforcement - Suggests shorthand classes where possible
- Canonical classes - Enforces canonical Tailwind class names
Enabling no-unknown-classes
The better-tailwindcss/no-unknown-classes rule is not enabled by default because it requires project-specific configuration:
import nordPlugin from "@nordhealth/eslint-plugin"
export default [
nordPlugin.configs.recommended,
{
settings: {
"better-tailwindcss": {
entryPoint: "./path/to/your/tailwind.css"
}
},
rules: {
"better-tailwindcss/no-unknown-classes": "warn"
}
}
]Custom Configuration
You can customize individual rules:
import nordPlugin from "@nordhealth/eslint-plugin"
export default [
nordPlugin.configs.recommended,
{
rules: {
// Enable migration rules
"@nordhealth/no-legacy-classes": ["warn", {
categories: ["spacing", "borders", "colors", "utilities"]
}],
// Change severity
"@nordhealth/logical-selectors": "error",
}
}
]Class Detection
The plugin detects classes across multiple frameworks and patterns:
Supported frameworks:
| Framework | Attributes detected |
|---|---|
| HTML/React | class, className |
| Vue | class, :class, v-bind:class |
| Svelte | class, class:directive |
| Angular | [class], [ngClass], [class.name] |
| Astro | class, class:list |
Supported utility functions:
| Function | Library |
|---|---|
clsx, classnames, classNames | clsx / classnames |
cn, cx | Common aliases |
cva | class-variance-authority |
tv | tailwind-variants |
twMerge, twJoin | tailwind-merge |
This means rules will work in code like:
// All of these are detected
<div className="n:p-m n:text-default" />
<div className={cn("n:p-m", condition && "n:text-weak")} />
<div className={clsx("n:flex", "n:gap-m")} />
const buttonVariants = cva("n:px-m n:py-s", {
variants: { size: { lg: "n:px-l n:py-m" } }
})