diff --git a/spec/66_component_taxonomy.md b/spec/66_component_taxonomy.md
new file mode 100644
index 00000000..421a4ef7
--- /dev/null
+++ b/spec/66_component_taxonomy.md
@@ -0,0 +1,927 @@
+# Component Taxonomy: 4-Layer Architecture for OMS·WMS·ERP SPA
+
+**Status**: DRAFT (Phase 0, requires Figma finalization)
+**Date**: 2026-07-26
+**Related**: [ADR-001 (Spec 65)](spec/65_adr_001_monolithic_spa_architecture.md), [OpenAPI (Spec 63)](spec/63_oms_wms_erp_api_openapi.yaml)
+
+---
+
+## Overview
+
+**Component Hierarchy**: 4 layers, 65 total components across OMS/WMS/ERP domains
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ Layer 4: Business Composite (11 CRUD Workflows) │
+│ └─ OrderForm, InventoryTransferWizard, VoucherEditor, etc. │
+├─────────────────────────────────────────────────────────────────┤
+│ Layer 3: Domain Fields (12 Domain-Specific Inputs) │
+│ └─ OrderLineField, InventoryField, VoucherLineField, etc. │
+├─────────────────────────────────────────────────────────────────┤
+│ Layer 2: Typed Fields (12 Type-Safe Inputs) │
+│ └─ TextField, DateField, CurrencyField, StatusField, etc. │
+├─────────────────────────────────────────────────────────────────┤
+│ Layer 1: Primitives (30 UI Building Blocks) │
+│ └─ Button, Input, Select, Table, Card, Badge, etc. │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+**Design System**: Tabler UI (Bootstrap 5) + Storybook 7.0+
+
+---
+
+## Layer 1: Primitive Components (30)
+
+### Purpose
+Reusable UI elements with **zero business logic**, full accessibility (WCAG 2.1 AA), typed props, consistent behavior.
+
+### Folder Structure
+```
+src/components/primitives/
+├─ Button/
+│ ├─ ButtonBase.vue
+│ ├─ ButtonBase.stories.ts
+│ └─ ButtonBase.spec.ts
+├─ Input/
+│ ├─ InputBase.vue
+│ ├─ InputBase.stories.ts
+│ └─ InputBase.spec.ts
+├─ Select/
+│ ├─ SelectBase.vue
+│ ├─ SelectBase.stories.ts
+│ └─ SelectBase.spec.ts
+├─ Table/
+│ ├─ TableBase.vue
+│ ├─ TableBase.stories.ts
+│ └─ TableBase.spec.ts
+├─ Card/
+│ ├─ CardBase.vue
+│ └─ CardBase.stories.ts
+├─ Badge/
+├─ Modal/
+├─ Checkbox/
+├─ Radio/
+├─ Textarea/
+├─ Pagination/
+├─ Alert/
+├─ Spinner/
+├─ Tooltip/
+├─ Dropdown/
+├─ Tabs/
+├─ Breadcrumb/
+├─ NavBar/
+├─ Sidebar/
+├─ Icon/
+└─ Link/
+```
+
+### Component Specifications
+
+| Component | Props | Events | A11y | Story |
+|-----------|-------|--------|------|-------|
+| **ButtonBase** | variant (primary/secondary/danger), size (sm/md/lg), disabled, loading | click | aria-label, focus-visible | 12 stories |
+| **InputBase** | type (text/email/number), placeholder, value, disabled, error, required | input, change, blur | label + aria-describedby (error) | 8 stories |
+| **SelectBase** | options: Array<{value, label}>, value, disabled, multiple | change | aria-label, aria-expanded | 10 stories |
+| **TableBase** | columns: Array<{key, header, sortable}>, data: any[], onSort | row-click, sort | semantic
, scope | 6 stories |
+| **CardBase** | title, subtitle, footer, clickable | click | semantic | 5 stories |
+| **BadgeBase** | status (success/danger/warning/info), size | — | aria-label | 8 stories |
+| **ModalBase** | isOpen, title, onClose | close | role="dialog", focus-trap | 6 stories |
+| **CheckboxBase** | value, label, disabled, required | change | aria-label, aria-describedby | 6 stories |
+| **RadioBase** | name, options, value, disabled | change | role="radiogroup" | 5 stories |
+| **TextareaBase** | value, placeholder, rows, disabled, error | input, change | aria-describedby | 5 stories |
+| **PaginationBase** | currentPage, totalPages, onPageChange | page-change | aria-label (next/prev) | 4 stories |
+| **AlertBase** | type (success/error/warning), dismissible, onDismiss | dismiss | role="alert" | 8 stories |
+| **SpinnerBase** | size, color | — | aria-busy | 4 stories |
+| **TooltipBase** | text, position (top/bottom/left/right) | show, hide | aria-describedby | 5 stories |
+| **DropdownBase** | trigger, items: Array<{label, action}>, onSelect | select | role="menu", role="menuitem" | 6 stories |
+| **TabsBase** | tabs: Array<{id, label, disabled}>, activeId, onTabChange | tab-change | role="tablist", role="tab" | 6 stories |
+| **BreadcrumbBase** | items: Array<{label, href}> | navigate | aria-label | 3 stories |
+| **NavBarBase** | title, items: Array<{label, href}>, sticky | navigate | semantic