Interactive dependency graph visualization for Xcode projects
A self-contained Lit web component that renders zoomable, filterable dependency graphs on canvas — designed for embedding in any web app or Swift server.
Docs • Live Demo • API Reference
- Interactive Canvas — Zoom, pan, and click through dependency graphs with hardware-accelerated Canvas2D rendering
- Cluster Layouts — Nodes grouped by project into visual clusters using a two-phase hierarchical layout (ELK + D3-force)
- Search & Filter — Filter by node type, origin, platform, or project; instant search with highlighting
- Transitive Dependencies — Highlight the full dependency chain (direct, transitive, dependents, or both) for any node
- Circular Dependency Detection — Automatically identifies and warns about cycles in the graph
- File Upload — Drag-and-drop or file picker for loading XcodeGraph JSON
- Theming — 30+ CSS custom properties for complete visual customization
- Dark & Light Mode — Automatic adaptation to
prefers-color-scheme, or force a specific mode - Accessible — ARIA labels, keyboard navigation, focus traps, and
prefers-reduced-motionsupport - TypeScript — Full type definitions with strict mode
- Zero Config — Single
<xcode-graph>element, works in any framework or vanilla HTML - CDN Ready — Self-contained ES module bundle with code-split Zod validation
# pnpm
pnpm add xcode-graph
# npm
npm install xcode-graph
# yarn
yarn add xcode-graphOr load directly from a CDN:
<script type="module" src="https://cdn.jsdelivr.net/npm/xcode-graph/dist/xcodegraph.js"></script><script type="module">
import 'xcode-graph';
</script>
<xcode-graph
.nodes=${nodes}
.edges=${edges}
></xcode-graph>const app = document.querySelector('xcode-graph');
app.loadRawGraph(xcodeGraphJson);loadRawGraph validates and transforms the raw JSON into the internal GraphNode / GraphEdge format automatically. The Zod validation layer is lazy-loaded on first call to keep the main bundle small. Unknown enum values produce warnings rather than errors, so the component is forward-compatible with newer Xcode project types.
<!DOCTYPE html>
<html>
<body>
<xcode-graph id="graph" style="width: 100%; height: 100vh;"></xcode-graph>
<script type="module">
import 'https://cdn.jsdelivr.net/npm/xcode-graph/dist/xcodegraph.js';
const res = await fetch('/graph.json');
const data = await res.json();
document.getElementById('graph').loadRawGraph(data);
</script>
</body>
</html><xcode-graph show-upload></xcode-graph>Users can drag-and-drop or use the file picker to load their own XcodeGraph JSON files.
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
nodes |
— | GraphNode[] |
undefined |
Graph nodes to visualize |
edges |
— | GraphEdge[] |
undefined |
Graph edges connecting nodes |
layoutOptions |
— | LayoutOptions |
undefined |
ELK + D3-force layout configuration overrides |
showUpload |
show-upload |
boolean |
false |
Show the file upload overlay |
colorScheme |
color-scheme |
'light' | 'dark' | 'auto' |
'auto' |
Color scheme preference |
| Method | Parameters | Description |
|---|---|---|
loadRawGraph() |
raw: unknown |
Load and transform raw XcodeGraph JSON (tuist graph --format json output) |
See the Component API reference for events, CSS custom properties, and sizing details.
GraphNode
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Unique identifier |
name |
string |
Yes | Display name |
type |
NodeType |
Yes | app, framework, library, package, test-unit, test-ui, cli |
platform |
Platform |
Yes | iOS, macOS, visionOS, tvOS, watchOS |
origin |
Origin |
Yes | local or external |
project |
string? |
No | Parent project or package name |
deploymentTargets |
DeploymentTargets? |
No | Min version per platform |
buildSettings |
BuildSettings? |
No | Swift version, compilation conditions, code signing |
bundleId |
string? |
No | Bundle identifier |
sourceCount |
number? |
No | Total source file count |
resourceCount |
number? |
No | Total resource file count |
destinations |
Destination[]? |
No | Supported device destinations |
tags |
string[]? |
No | Metadata tags |
foreignBuild |
ForeignBuildInfo? |
No | External build system info (Bazel, CMake, KMP/Gradle) |
GraphEdge
| Field | Type | Required | Description |
|---|---|---|---|
source |
string |
Yes | ID of the source node (depends on target) |
target |
string |
Yes | ID of the target node (dependency) |
kind |
DependencyKind? |
No | target, project, sdk, xcframework |
platformConditions |
Platform[]? |
No | Platform conditions for this edge |
See the full type definitions in the Data Types reference.
The component is fully themeable via CSS custom properties. Set --graph-* properties on the <xcode-graph> element or any ancestor:
xcode-graph {
/* Core */
--graph-bg: #1a1a2e;
--graph-bg-secondary: #16213e;
--graph-text: #e2e8f0;
--graph-text-muted: rgba(226, 232, 240, 0.5);
--graph-accent: #7c3aed;
--graph-accent-dim: rgba(124, 58, 237, 0.15);
--graph-border: rgba(255, 255, 255, 0.08);
--graph-canvas-bg: #1a1a2e;
--graph-radius: 8px;
/* Node type colors */
--graph-node-app: #f59e0b;
--graph-node-framework: #0ea5e9;
--graph-node-library: #22c55e;
--graph-node-test: #ec4899;
--graph-node-cli: #3b82f6;
--graph-node-package: #eab308;
/* Platform colors */
--graph-platform-ios: #007AFF;
--graph-platform-macos: #64D2FF;
--graph-platform-tvos: #B87BFF;
--graph-platform-watchos: #5AC8FA;
--graph-platform-visionos: #7D7AFF;
/* Typography */
--graph-font: 'Inter', system-ui, sans-serif;
--graph-font-mono: 'JetBrains Mono', ui-monospace, monospace;
}The component adapts to prefers-color-scheme automatically. Both dark and light token sets are built in. To force a specific mode:
<xcode-graph color-scheme="light"></xcode-graph>
<xcode-graph color-scheme="dark"></xcode-graph>
<xcode-graph color-scheme="auto"></xcode-graph> <!-- default -->See the Component API reference for all available properties with dark and light mode defaults.
xcode-graph supports modern browsers with ES2020+ module support:
| Browser | Minimum Version |
|---|---|
| Chrome | 80+ |
| Firefox | 80+ |
| Safari | 14+ |
| Edge | 80+ |
The component uses ES modules, Canvas2D rendering, and CSS custom properties. No polyfills are required for supported browsers.
The graph uses a two-phase layout pipeline:
- Macro layout (ELK) — Positions clusters (groups of related nodes) using the ELK layered algorithm for hierarchical structure
- Micro layout (D3-force) — Positions nodes within each cluster using physics-based force simulation with boundary constraints, radial orbits, and collision avoidance
Override layout parameters via the layoutOptions property:
const app = document.querySelector('xcode-graph');
app.layoutOptions = {
configOverrides: {
elkDirection: 'RIGHT', // LEFT | RIGHT | UP | DOWN
elkNodeSpacing: 300,
elkLayerSpacing: 400,
iterations: 500,
},
hooks: {
onLayoutComplete: (result) => {
console.log('Layout done:', result.clusters.length, 'clusters');
},
},
};See the Layout Configuration reference for all available parameters.
src/
├── components/ Root <xcode-graph> web component
├── graph/
│ ├── components/ Canvas rendering (Canvas2D, nodes, edges, tooltips)
│ ├── controllers/ Layout orchestration, interaction handling, animation loop
│ ├── layout/ ELK hierarchical + D3-force physics pipeline
│ ├── signals/ Graph state (selection, highlights, view mode)
│ └── utils/ Traversal, filtering, canvas theming
├── services/
│ ├── graph-data-service.ts O(1) indexed data layer
│ ├── graph-analysis-service.ts Circular dependency & path detection
│ ├── xcode-graph.service.ts Raw Tuist JSON → internal format
│ ├── xcode-graph.validation.ts Zod schema validation
│ ├── graph-loader.ts Progressive loading
│ ├── graph-stats-service.ts Graph statistics
│ └── error-service.ts User-facing error handling
├── shared/
│ ├── schemas/ Type definitions (GraphNode, GraphEdge, enums)
│ ├── signals/ Global state (filters, UI, errors)
│ ├── controllers/ Zag-js, keyboard shortcuts, focus traps, resize
│ └── machines/ Zag-js state machines
├── ui/
│ ├── layout/ Tab orchestrator, header, sidebar
│ ├── components/ 80+ UI components (filters, panels, search, badges)
│ └── utils/ Colors, icons, sizing, viewport
├── styles/
│ ├── tokens.css Design tokens (dark + light mode)
│ └── theme-utils.ts Color manipulation, contrast checking
└── index.ts Library entry point (re-exports public API)
| Concern | Technology |
|---|---|
| Web components | Lit 3.x |
| State management | Lit Signals (@lit-labs/signals) |
| UI state machines | Zag-js |
| Hierarchical layout | ELK.js |
| Physics simulation | D3-force |
| Schema validation | Zod 4.x |
| Color utilities | colord |
| Focus management | focus-trap |
| Service | Purpose |
|---|---|
GraphDataService |
Centralized O(1) data layer with indexed lookups by type, project, platform, and origin. Provides dependency queries (direct, transitive, BFS traversal) and cluster support. |
GraphAnalysisService |
Stateless algorithms for graph analysis: BFS path detection and DFS cycle finding. |
XcodeGraphService |
Transforms raw Tuist/XcodeGraph JSON into the internal GraphData format. Forward-compatible — unknown enum values produce warnings, not crashes. |
ErrorService |
Singleton error handling with toast notifications. Auto-dismiss timers, severity levels, and user-facing messages. |
GraphStatsService |
Computes graph statistics (node counts, edge counts, connectivity metrics). |
GraphLoaderService |
Progressive/lazy loading with progress tracking. |
The component uses Lit Signals for reactive state:
- Graph signals —
selectedNode,selectedCluster,hoveredNode,circularDependencies, highlight toggles - Filter signals — Active filters for node types, platforms, origins, projects, packages
- UI signals — Zoom level, search query, animation toggle, active tab
- Display computed — Derived filtered/highlighted data based on all active filters and selections
- Error signals — Active error/warning state
xcode-graph is designed to work with Swift servers. A reference SwiftNIO implementation serves the component from CDN with zero bundled assets:
GET / → HTML page loading <xcode-graph> from jsdelivr
GET /graph.json → Raw XcodeGraph JSON data
import XcodeGraphServer
let server = try GraphServer(graph: graph)
try server.start() // opens browser, blocks until ctrl-cThe Swift package includes:
- XcodeGraphServer — SwiftNIO HTTP server (binds to
localhost:8081) - XcodeGraphCLI — Command-line interface
- TransformGraph — Swift code analyzer for type extraction
See the Swift Integration guide for details.
The package ships metadata files for editor integration:
| File | Editor | Purpose |
|---|---|---|
custom-elements.json |
All | Custom Elements Manifest (CEM) |
web-types.json |
WebStorm/JetBrains | Component and property completions |
vscode.html-custom-data.json |
VS Code | HTML tag and attribute IntelliSense |
vscode.css-custom-data.json |
VS Code | CSS custom property completions |
pnpm install # Install dependencies
pnpm dev # Start dev server (port 3000)
pnpm test:run # Run unit tests
pnpm test:coverage # Run tests with coverage
pnpm check # Lint + format (Biome) + token validation
pnpm build:lib # Build production library bundle
pnpm docs:dev # Start docs dev server (port 5174)
pnpm docs:api # Generate TypeDoc API reference
pnpm analyze # Regenerate Custom Elements Manifest
pnpm size # Check bundle size limits
pnpm size:why # Analyze bundle composition
pnpm knip # Check for unused dependencies and exportsThe project runs six GitHub Actions workflows:
| Workflow | Trigger | Purpose |
|---|---|---|
build.yml |
Push / PR | Lint, test with coverage, type-check, verify CEM manifest, verify API surface, build |
publish.yml |
Git tag v* |
Version alignment check, test, build, publish to npm |
docs.yml |
Push to main | Build and deploy VitePress + TypeDoc docs to GitHub Pages |
audit.yml |
Schedule | Security audit of dependencies |
size-limit.yml |
Push / PR | Bundle size monitoring (main: 210 kB gzip, all chunks: 785 kB gzip) |
compat-check.yml |
Push / PR | Compatibility testing |
Tests use Vitest with both jsdom (unit) and Playwright (browser) environments:
pnpm test:run # Run all tests once
pnpm test # Watch mode
pnpm test:coverage # With V8 coverage (text, HTML, LCOV)Test coverage includes services, schemas, signals, controllers, utilities, layout algorithms, UI components, web workers, accessibility (vitest-axe), and integration tests.
Full docs at ajkolean.github.io/xcode-graph
- Getting Started — Installation, usage, data shape, theming, and color scheme
- Examples — Common usage patterns (embedded, file upload, CDN, theming)
- Swift Integration — Embedding in a Swift server with SwiftNIO
- Architecture — Component tree, data flow, layout pipeline
- Troubleshooting — FAQ and common issues
- Component API — Properties, methods, events, CSS custom properties
- Data Types — GraphNode, GraphEdge, enums, and all type definitions
- Layout Configuration — ELK & D3-force parameters and lifecycle hooks
- API Reference — Full TypeDoc reference (100+ exported functions, classes, and types)
MIT © Andy Kolean
