Documentation Index
Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
Use this file to discover all available pages before exploring further.
Version: 2.0.0
Last Updated: 2026-05-13
Scope: All modules. Sizes/colors: UI_UX_STANDARDS.md § Icons, SEMANTIC_COLORS.md.
Single reference for icon assignment, sizing, imports, and accessibility.
0. Direction A — Final Rules (canonical)
These rules are non-negotiable for all new and refactored UI. PR review and the
npm run audit:icons script enforce them.
-
Library: Lucide-react only. Render through
<AppIcon> from
@/shared/ui/icon. Raw <LucideIcon … /> JSX is allowed only inside
leaf primitives we don’t own (e.g. shadcn shells); everywhere else use
AppIcon.
-
Stroke width:
1.5 (Apple-Minimal Outline). Never set
strokeWidth inline on a Lucide tag — use <AppIcon> so the platform
default applies, or pass weight="thin" | "regular" | "bold" for the
rare exceptions (1.1 / 1.5 / 2.0).
-
Size scale (locked, 4 tiers):
xs/14, sm/16, md/20, lg/24.
No other sizes. 2xs/12 exists only for legacy dense table chrome and
must not be used in new code.
-
Active-state behavior (navigation): the active item swaps the
outline glyph for a filled variant —
fill="currentColor" and
strokeWidth={0} — paired with the text-primary tone. Use the
getActiveIconSvgProps(isActive) helper in
src/platform/navigation/utils/active-state.ts. Idle items stay
outline at text-muted-foreground.
-
Color tokens (allow-list): the only colors permitted on an icon
are the semantic tones below — applied via the
tone prop, never
hex/text-gray-*/text-blue-* literals.
tone value | CSS class | When to use |
|---|
muted | text-muted-foreground | Default for idle nav, secondary chrome |
neutral | text-foreground | Body-level inline icons |
primary | text-primary | Active nav, primary CTAs |
accent | text-accent | Brand-accent surfaces |
onAccent | text-accent-foreground | Icons over bg-accent/bg-primary |
success | text-success | Confirmations, positive deltas |
warning | text-warning | Caution, attention |
destructive | text-destructive | Errors, delete, irreversible |
info | text-info | Info chips, neutral system messages |
current | text-current | Inherit current text color |
inherit | (none) | Caller controls color via parent |
Anything outside this list — including raw palette classes
(text-blue-500, #0EA5E9) and per-module hex values — is rejected by
the icon audit and PR review.
-
Variants:
variant="outline" (default) for the line look, or
variant="solid" to fill the glyph with currentColor (zeroes the
stroke). The active-state filled treatment in rule 4 is the canonical
use of solid.
-
Accessibility: decorative icons (default) get
aria-hidden="true"
automatically; meaningful icons must pass decorative={false} and a
non-empty aria-label (TypeScript enforces this).
// ✅ canonical idle nav item
<AppIcon icon={Inbox} size="sm" tone="muted" />
// ✅ canonical active nav item (filled, primary tone)
<AppIcon icon={Inbox} size="sm" tone="primary" variant="solid" />
// ✅ icon-only button
<AppIcon icon={Trash2} size="sm" tone="destructive" decorative={false} aria-label="Delete row" />
// ❌ raw Lucide with palette color and inline stroke
<Inbox className="h-4 w-4 text-blue-500" strokeWidth={1.75} />
1. Quick Reference
Size tokens (Tailwind)
The scale is locked to four tiers. Do not introduce new sizes.
AppIcon size | Pixels | Tailwind | Use case |
|---|
xs | 14px | h-3.5 w-3.5 | Dense rows, inline-with-text, table chrome |
sm | 16px | h-4 w-4 | List items, buttons, form controls, sub-nav |
md (default) | 20px | h-5 w-5 | Top-level nav items, section headers |
lg | 24px | h-6 w-6 | Empty states, feature/illustrative icons |
Anything larger (32 / 48 px) is brand or illustration, not an icon —
render the brand SVG (public/brand/*) or a dedicated illustration
component, not a Lucide glyph.
AppIcon wrapper (preferred for shared/component code)
AppIcon from @/shared/ui/icon is the required way to render a Lucide icon in shared, platform, and core code. It enforces the locked stroke width, size scale, and color allow-list, and standardizes the decorative-vs-meaningful accessibility contract.
import { Inbox, Trash2 } from 'lucide-react';
import { AppIcon } from '@/shared/ui/icon';
// Decorative (defaults to aria-hidden="true")
<AppIcon icon={Inbox} size="lg" tone="muted" />
// Meaningful (TypeScript requires aria-label)
<AppIcon icon={Trash2} size="sm" decorative={false} aria-label="Delete row" />
| Prop | Default | Notes |
|---|
size | md (20px) | xs/14, sm/16, md/20, lg/24 — maps 1:1 to the table above. |
strokeWidth | 1.5 | Platform default (Direction A). Lucide ships 2; we run lighter for the Apple-Minimal Outline feel. Override only via weight. |
weight | regular | thin (1.1) / regular (1.5) / bold (2.0). Ignored when strokeWidth is passed explicitly. |
variant | outline | solid fills the glyph with currentColor and zeroes the stroke. Used for the active-nav treatment. |
tone | — | Semantic color shorthand. See the allow-list in §0. Caller-provided className color tokens still win. |
decorative | true | Sets aria-hidden="true". When false, aria-label is required by TypeScript. |
className | — | Merged via cn. Prefer tone for color; reserve className for layout (shrink-0, mr-2, transition-transform). |
Raw <LucideIcon className="h-4 w-4" /> is no longer acceptable in new code. Existing leaf-primitive usages are tracked in reports/icon-audit-latest.json and must be migrated when the surrounding file is touched.
Import pattern
import { LayoutDashboard, Users, Building2 } from "lucide-react";
Accessibility
- Icon-only buttons:
aria-label="Action name".
- Decorative (with visible text):
aria-hidden="true" on the icon.
1.5 Approved Semantic Icons — Quick Reference
A scannable cheat sheet curated from §2 (universal/status/compliance), §5 (wizard step keys), §6 (module + healthcare), and §7 (anti-patterns). For full detail, jump to those sections.
Audit: Run npm run audit:icons to find raw Lucide / inline-stroke / hardcoded-color offenders in src/shared/components/** and src/platform/**. Latest snapshot JSON: reports/icon-audit-latest.json.
Approval audit: Run npm run audit:icon-approvals (or …:strict to fail CI) to flag deprecated Lucide imports (e.g. CheckCircle, AlertCircle) and DO NOT CHANGE mapping violations (context phrase + wrong icon in same JSX). Report: reports/icon-approvals-latest.json.
Approved icons by concept
| Domain | Concept | Icon | Token / step key | Notes |
|---|
| Universal | Dashboard | LayoutDashboard | — | Every module landing |
| Universal | Settings | Settings | — | Config / preferences |
| Universal | Create / Add | Plus | — | Primary create CTA |
| Universal | People list | Users | — | Directory views |
| Universal | Teams / groups | UsersRound | — | Group views |
| Universal | Forms / docs | FileText | — | Templates, generated docs |
| Universal | Reports | BarChart3 | — | Analytics |
| Universal | Schedule | CalendarDays | — | Scheduling (NOT date pickers) |
| Universal | Time | Clock | — | Time tracking |
| Status & Alerts | Approved / Complete | CheckCircle2 | complete | Pair with text-success |
| Status & Alerts | Warning | AlertTriangle | — | Non-blocking caution |
| Status & Alerts | Error / blocking | CircleAlert | — | Replaces deprecated AlertCircle |
| Status & Alerts | Info | Info | — | Tooltips, callouts |
| Status & Alerts | Disciplinary (HR PIP) | AlertOctagon | — | NOT TrendingDown |
| Status & Alerts | Emergency / incident | Siren | — | NOT AlertOctagon |
| Compliance & Security | Compliance / verified | ShieldCheck | — | NOT generic Shield |
| Compliance & Security | Risk (GR register) | ShieldAlert | — | NOT AlertTriangle |
| Compliance & Security | Generic protection | Shield | — | Reserve for non-compliance security |
| Compliance & Security | Audit log | ScrollText | — | Audit trails |
| Healthcare | Medication | Pill | — | E-prescribing, MAT, med list |
| Healthcare | Vitals | HeartPulse | — | NOT Heart / Activity alone |
| Healthcare | Clinical / provider | Stethoscope | — | Clinical workflows |
| Healthcare | Injection / immunization | Syringe | — | Vaccines, injectables |
| Healthcare | Patient chart | ClipboardList | — | Chart views |
Healthcare copy-paste templates: see examples/wizard-templates/healthcare-icon-keys.json for ready-to-use PF-41 snippets using stethoscope, pill, and syringe.
| Wizard steps | Personal info | User | user | PF-41 template key |
| Wizard steps | Contact info | Mail | mail | PF-41 template key |
| Wizard steps | Schedule | CalendarDays | calendar | PF-41 template key |
| Wizard steps | Review & submit | ClipboardCheck | review | PF-41 template key |
| Wizard steps | Complete | CheckCircle2 | complete | PF-41 template key |
| Wizard steps | Unknown key | CircleHelp | (fallback) | Auto-rendered + dev console warning by resolveStepIcon() |
| Module-specific | HR | Users | — | See §6 |
| Module-specific | CL | Stethoscope | — | See §6 |
| Module-specific | PM | ClipboardList | — | See §6 |
| Module-specific | FA | DollarSign | — | See §6 |
| Module-specific | RH | Home | — | Recovery housing (residential) |
| Module-specific | GR | ShieldCheck | — | Governance & risk |
| Module-specific | CE | Megaphone | — | Community engagement |
Preferred vs. deprecated variants
| Don’t use | Use instead | Why |
|---|
CheckCircle | CheckCircle2 | Lucide deprecated outline variant |
AlertCircle | AlertTriangle or CircleAlert | AlertCircle removed; pick by semantic |
Home (for module landings) | LayoutDashboard | Module landings use Dashboard, not Home |
Shield (for compliance) | ShieldCheck | Shield reserved for generic protection |
TrendingDown (for PIPs) | AlertOctagon | Performance vs. disciplinary distinction |
Calendar (for scheduling) | CalendarDays | Calendar/CalendarIcon reserved for date pickers |
Raw <LucideIcon className="h-4 w-4" /> in shared components | <AppIcon icon={...} size="sm" /> | Enforces stroke width + a11y contract |
DO NOT CHANGE mappings
These mappings are locked across cores. Changing them creates cross-module visual drift or breaks template-driven wizards.
| Mapping | Why it’s locked | Example of breakage if changed |
|---|
Dashboard → LayoutDashboard | Used by every module landing page; nav muscle memory | Swapping to Home breaks visual parity across HR/CL/PM/RH/FA |
Settings → Settings (gear) | Universal config affordance | A wrench/sliders icon would suggest “tools” not “preferences” |
Approved/Complete → CheckCircle2 | Paired with text-success everywhere | CheckCircle (deprecated) renders thinner — visual drift in approval flows |
Risk → ShieldAlert (NOT AlertTriangle) | GR risk register is shield-coded; warnings are triangle-coded | Conflates “risk” with “warning” in audit reports |
Incidents → Siren (NOT AlertOctagon) | Disciplinary (HR) uses AlertOctagon; emergencies use Siren | HR PIPs and clinical incidents would visually collide |
Compliance → ShieldCheck (NOT Shield) | Shield = generic security; ShieldCheck = verified/compliant | Credentialing badges lose the “verified” cue |
Medication → Pill (NOT Capsule) | E-prescribing, MAT, med list all standardized on Pill | Mixed iconography in clinical med views |
Vitals → HeartPulse (NOT Heart or Activity alone) | Heart = favorite/like; Activity = generic monitoring | Vitals dashboard mistaken for favorites/analytics |
Wizard “Personal info” step → User (key user) | PF-41 templates resolve string keys via resolveStepIcon() | Renaming the key breaks every template JSON referencing it |
Wizard “Complete” step → CheckCircle2 (key complete) | Same template JSON contract | Template-driven wizards render fallback icon |
Quick example
// ✅ Approved
<AppIcon icon={ShieldCheck} size="sm" className="text-success" />
<AppIcon icon={CheckCircle2} size="sm" decorative={false} aria-label="Approved" />
// ❌ Avoid
<CheckCircle className="h-4 w-4 text-green-500" /> // deprecated icon + raw color
<Shield className="h-4 w-4" /> // generic, not "compliant"
Tone & variant (v1.3.0)
AppIcon accepts three additive props that eliminate hand-typed semantic
color classes and stroke overrides. All are optional — omitting them is
byte-identical to the legacy behavior.
| Prop | Values | Effect |
|---|
tone | success warning destructive info muted neutral primary accent onAccent current inherit | Applies the matching text-* semantic token. accent = text-accent (default on subtle accent tints like bg-accent/10), onAccent = text-accent-foreground (for solid bg-accent surfaces). current = inherit from parent; inherit = no class added. |
variant | outline (default) solid | solid sets fill="currentColor" and zeroes the stroke for filled glyphs (e.g., CheckCircle2 in success badges). |
weight | thin (1.25) regular (1.75) bold (2.25) | Stroke shorthand. Explicit strokeWidth={n} always wins. |
Resolution order on the rendered SVG (last wins): size class → tone class →
caller className. So one-off escapes like className="text-amber-700"
still override a tone.
// Before — hand-typed token, easy to drift
<AppIcon icon={CheckCircle2} size="sm" className="text-success" />
// After — tone shorthand, registry-backed
<AppIcon icon={CheckCircle2} size="sm" tone="success" />
// Filled glyph for solid badges
<AppIcon icon={CircleAlert} size="sm" tone="destructive" variant="solid" />
Solid variant cookbook
variant="solid" flips the glyph from outlined to filled (sets
fill="currentColor" and zeroes the stroke). It’s a visual weight
choice, not a semantic one — tone still carries the meaning. Reach for
solid only when the icon needs to read as a single dense anchor:
a tinted badge chip, a status pip in dense tables, an avatar overlay,
or an icon-in-circle hero on an empty/error state. Default to
outline everywhere else (toolbars, nav, body copy, form affordances).
| Pattern | When to use solid | Recommended icons |
|---|
| Status badge — terminal/severe | Final states that need to “pop” in a list (paid, denied, no-show, void, overdue, failed). Pair with StatusBadge’s iconVariant: 'solid'. | CheckCircle2, XCircle, CircleAlert, OctagonAlert |
| Inline severity pip | 14–16 px dot beside a row label where an outline would look like a circle, not a status. | CircleDot, CircleAlert, Circle |
| Hero icon-in-circle | ErrorState, empty-state cards, onboarding callouts where the icon sits in a tinted bg-{tone}/10 circle. | CircleAlert, Info, CircleHelp, ShieldAlert |
| Avatar / chip overlay | Small status overlay on an avatar (verified, locked, on-call). | BadgeCheck, ShieldCheck, Lock |
| Toast / notification leading icon | Sonner toast variants where a filled glyph improves contrast against the toast surface. | CheckCircle2, CircleAlert, Info |
Don’t use solid for:
- Toolbar / icon-button affordances (
Pencil, Trash2, Search,
Filter) — outline is the default app feel.
- Navigation rails and sidebar items — outline keeps the chrome quiet.
- Decorative inline icons next to body text — solid will dominate the
line.
- Anything where the icon is larger than
lg (24 px) — at hero sizes
the filled shape can read as a logo.
// Status badge — solid for terminal/severe states
import { CheckCircle2, XCircle, CircleAlert } from 'lucide-react';
import { createStatusBadge } from '@/shared/components/StatusBadge';
type ClaimStatus = 'paid' | 'pending' | 'denied' | 'overdue';
export const ClaimStatusBadge = createStatusBadge<ClaimStatus>({
paid: { label: 'Paid', variant: 'success', icon: CheckCircle2, iconTone: 'success', iconVariant: 'solid' },
pending: { label: 'Pending', variant: 'warning', icon: CircleAlert, iconTone: 'warning' /* outline */ },
denied: { label: 'Denied', variant: 'destructive', icon: XCircle, iconTone: 'destructive', iconVariant: 'solid' },
overdue: { label: 'Overdue', variant: 'destructive', icon: CircleAlert, iconTone: 'destructive', iconVariant: 'solid' },
});
// Hero icon-in-circle — solid reads as a single anchor inside the tint
<div className="rounded-full bg-destructive/10 p-3">
<AppIcon icon={CircleAlert} size="lg" tone="destructive" variant="solid" />
</div>
// Sonner toast leading icon
toast.success('Claim submitted', {
icon: <AppIcon icon={CheckCircle2} size="sm" tone="success" variant="solid" />,
});
// ❌ Don't: solid on a toolbar button — too heavy
<AppIcon icon={Pencil} size="sm" variant="solid" />
// ✅ Do:
<AppIcon icon={Pencil} size="sm" tone="muted" />
Where this is already wired in shared components
ErrorState — hero icon uses variant="solid" inside the tinted
circle wrapper (destructive and warning severities).
StatusBadge / createStatusBadge<T>() — accepts
iconVariant: 'solid' | 'outline' per status key, so domain badges
opt in to solid only for the states that warrant the extra weight.
For status-driven UIs, derive the tone from the registry instead of
hard-coding it:
import { semanticStatusToIconTone } from '@/shared/lib/semantic-colors';
<AppIcon icon={Icon} size="xs" tone={semanticStatusToIconTone(status)} />
Domain status aliases (v1.4.0)
Many UIs render statuses that aren’t one of the six core
SemanticStatus values — workflow lifecycle (draft, in_progress,
completed), approvals (approved, pending, rejected), scheduling
(confirmed, cancelled, no_show), billing (paid, overdue,
void), and clinical (discharged, prospect). These are all
aliased to existing AppIconTones via DOMAIN_STATUS_TONE — no new
status tokens, and no status-specific tone expansion. Use
domainStatusToIconTone() so the same DB enum string drives the icon
tone consistently across modules.
import { domainStatusToIconTone } from '@/shared/lib/semantic-colors';
// CE lead, PM claim, FW workflow run — all use the same helper.
<AppIcon icon={CheckCircle2} size="sm" tone={domainStatusToIconTone(lead.status)} />
<AppIcon icon={CircleAlert} size="sm" tone={domainStatusToIconTone(claim.status)} variant="solid" />
| Lifecycle | Status keys → tone |
|---|
| Approval / review | approved → success · denied, rejected → destructive · pending → warning · in_review, submitted → info |
| Workflow / task | active, completed → success · in_progress, queued, scheduled → info · on_hold, paused → warning · expired, failed → destructive · archived, cancelled, draft → muted |
| Scheduling / attendance (CE / PM) | attended, confirmed → success · no_show → destructive |
| Billing / claims (PM / FA) | paid → success · partial → warning · overdue → destructive · void → muted |
| Clinical / patient (CL) | discharged → neutral · inactive → muted · prospect → info |
Notes:
- Input is normalized (
PascalCase, kebab-case, Title Case all
work), so DB enums can be passed verbatim.
- Unknown keys fall back to
tone="muted" — the UI never breaks on a
new/unmapped status, mirroring the wizard-step icon fallback (§5).
- To add a new status, edit
DOMAIN_STATUS_TONE and this table only;
do not introduce a new AppIconTone value.
Cross-references
- Full universal/status/compliance tables → §2 (Assignment Principles).
- Wizard step string keys +
resolveStepIcon() contract → §5.
- Module + healthcare icon catalog → §6.
- Anti-patterns and deprecations → §7.
2. Assignment Principles
- Semantic consistency: Same concept → same icon across modules.
- Visual differentiation: Similar but distinct concepts → different icons.
- Accessibility: Sufficient contrast; pair with text labels.
Universal icons (DO NOT CHANGE)
| Icon | Meaning | Usage |
|---|
LayoutDashboard | Overview/Dashboard | Module landing, dashboard views |
Settings | Settings/Configuration | Module settings, preferences |
FileText | Forms/Documents | Forms, documents, templates |
BarChart3 | Reports/Analytics | Reports, analytics |
Plus | Create/Add | Create buttons, add actions |
Users | People List | Employee lists, user directories |
UsersRound | Teams/Groups | Team pages, group views |
Calendar | Date/Schedule | Calendars, scheduling, dates |
Clock | Time | Time tracking |
CheckCircle2 | Complete/Approved | Approvals, completed items |
Building2 | Organization/Department | Departments, vendors, organizations |
Avoid: CheckCircle (deprecated) — use CheckCircle2. Scheduling: Use CalendarDays for scheduling; Calendar/CalendarIcon for date pickers.
Alert & status (do not interchange)
| Icon | Meaning | Use For |
|---|
AlertTriangle | General warning | Warnings, caution |
CircleAlert | Issues/Problems | Issues list, workflow alerts, findings |
ShieldAlert | Risk/Security risk | Risk registers, security risks |
Siren | Incidents/Emergency | Incidents, emergencies |
AlertOctagon | Serious/Disciplinary | PIPs, disciplinary, critical alerts |
Flag | Events/Flags | Significant events, flagged items |
Bug | Vulnerabilities | Security vulnerabilities, bugs |
Compliance & security
| Icon | Meaning | Use For |
|---|
Shield | Security/Protection | Security features |
ShieldCheck | Compliance/Verified | Compliance, credentialing |
ClipboardCheck | Audit/Checklist | Audits, checklists |
Award | Certification | CEUs, certificates, accreditations |
3. Size, color, and usage patterns
Authority: Scale in src/index.css (--icon-xs … --icon-lg); UI_UX_STANDARDS.md § Icons.
// Standard
<Button>
<Plus className="h-5 w-5 mr-2" />
Add New
</Button>
// Semantic color
<ShieldCheck className="h-5 w-5 text-success" />
<UserMinus className="h-5 w-5 text-destructive" />
Color: Use semantic tokens (text-success, text-warning, text-destructive, text-muted-foreground). Do not use text-blue-500, text-green-600, etc.
4. Accessibility
- Icon-only buttons: Always
aria-label (e.g. aria-label="View dashboard").
- Decorative icon next to text:
aria-hidden="true" on the icon.
- See UI_UX_STANDARDS.md § Accessibility for focus and ARIA.
Example:
<button aria-label="View dashboard">
<LayoutDashboard className="h-5 w-5" />
</button>
<div>
<Clock className="h-5 w-5" aria-hidden="true" />
<span>Time Clock</span>
</div>
5. Wizard step icons
Wizard steps use icons inside step circle indicators for visual orientation. The platform provides a curated catalog at src/platform/wizards/utils/stepIcons.ts.
Two usage modes:
| Context | Icon type | How to pass |
|---|
WizardShell (presentational) | Lucide component | step.icon = User |
| PF-41 template JSON | String key | step.icon = "user" → resolve via resolveStepIcon() |
Common step icon assignments:
| Step purpose | Icon key | Lucide component |
|---|
| Personal info | user | User |
| Contact details | mail | Mail |
| Dates/scheduling | calendar | Calendar |
| Employment/role | briefcase | Briefcase |
| Finance/payment | wallet | Wallet |
| Documents | file-text | FileText |
| Credentials | shield-check | ShieldCheck |
| Configuration | settings | Settings |
| Review/summary | review | ListChecks |
| Completion | complete | CheckCircle2 |
| Clinical assessment | stethoscope | Stethoscope |
| Medication / Rx | pill | Pill |
| Injection / lab draw | syringe | Syringe |
| Vitals / biometrics | heart-pulse | HeartPulse |
| Behavioral health | brain | Brain |
| Lab / diagnostics | microscope | Microscope |
| Treatment / wound care | bandage | Bandage |
| Symptom / temperature | thermometer | Thermometer |
| Health monitoring | activity | Activity |
| Clinical checklist | clipboard-list | ClipboardList |
Sizing: size-4 (16px) inside step circles. Step circles are size-9 (desktop) / size-11 (mobile).
See: WIZARD_DEVELOPMENT_GUIDE.md § Step Icons and WIZARD_UX_STANDARD.md § Step icons.
6. Module-specific patterns (summary)
| Module | Concept | Icon |
|---|
| HR | Onboarding / Offboarding | UserPlus / UserMinus |
| HR | Credentialing / PIPs | ShieldCheck / AlertOctagon |
| RH | Residences / Significant Events | Building2 / Flag |
| LO | Goals / Issues / Scorecards | Target / CircleAlert / BarChart3 |
| GR | Policy / Risk / Audit | FileText / ShieldAlert / CircleAlert |
| IT | Assets / Tickets / Vulnerabilities | Laptop / Ticket / Bug |
Healthcare & clinical (CL / PM)
| Concept | Icon | Use For |
|---|
| Clinical exam / vitals | Stethoscope | CL intake, vitals capture, clinical assessment |
| Medication / Rx | Pill | E-prescribing, MAT, medication list |
| Injection / lab draw | Syringe | Vaccinations, injectable medications, lab orders |
| Vitals / biometrics | HeartPulse | Vitals dashboard, biometrics |
| Behavioral health | Brain | Mental health assessments, COD documentation |
| Lab / diagnostics | Microscope | Lab results, diagnostics |
| Treatment / wound care | Bandage | Treatment plans, wound documentation |
| Symptoms / temperature | Thermometer | Symptom screening, fever check |
| Health monitoring | Activity | Health metrics, monitoring dashboards |
| Clinical checklist | ClipboardList | Intake forms, screening checklists |
7. Anti-patterns
- Do not use
AlertTriangle for everything; differentiate by context.
- Do not use
TrendingDown for negative performance; use AlertOctagon for PIPs.
- Do not use
Shield for compliance; use ShieldCheck.
- Do not use icons without labels for interactive elements.
- Do not
import * as Icons from "lucide-react"; import only what you use.
Deprecated: CheckCircle → CheckCircle2; AlertCircle → AlertTriangle or CircleAlert; Home → LayoutDashboard.
8. Adding new icons
- Search Lucide icons.
- Check if a similar concept exists in this guide.
- Prefer semantic icons; document the new icon here before use.
References
Lint enforcement
AppIcon usages are guarded by scripts/audit/audit-appicon-tones.ts, wired into validate:governance. Four rules keep tone as the single source of truth for icon color:
- APPICON-001 (failure) —
className carries a semantic color token (e.g. text-success) and tone is set. Pick one.
- APPICON-002 (failure) —
className carries a Tailwind palette class (e.g. text-amber-700). Use tone or a semantic token; palette classes don’t react to dark mode or PF-95 tenant theming.
- APPICON-003 (failure) —
tone literal isn’t a member of AppIconTone (typos like tone="successful").
- APPICON-004 (warning) —
variant="solid" paired with weight=… (weight is ignored when solid forces stroke-width 0).
Run locally: npm run audit:appicon-tones (human table) or npm run audit:appicon-tones:json (writes reports/audit-appicon-tones.json). Dynamic tone={…} / className={cn(...)} expressions are skipped with an info log, never failed.
Autofix
Most APPICON-001/002/004 violations are mechanical. Run npm run audit:appicon-tones:fix to rewrite them in place: dropping duplicate semantic className tokens (Fix A), converting standalone text-success/text-muted-foreground/etc. classes into the matching tone shorthand (Fix B), and removing dead weight props when variant="solid" is set (Fix D). Use npm run audit:appicon-tones:fix:dry to preview rewrites in reports/audit-appicon-tones-fix-preview.json without touching files.
npm run audit:appicon-tones:fix:aggressive additionally maps Tailwind palette classes (text-amber-700 → tone="warning", etc.) to the closest semantic tone (Fix C). Palette → tone mapping is judgment-laden — always diff-review before committing. APPICON-003 typos (e.g. tone="successful") and dynamic className={cn(...)} expressions are never autofixed; the script reports them for manual fix.