{"id":31286831,"url":"https://github.com/aqu1tain/sandjs","last_synced_at":"2026-01-22T11:01:47.249Z","repository":{"id":315030879,"uuid":"1057795756","full_name":"Aqu1tain/sandjs","owner":"Aqu1tain","description":"Sand.js (Sunburst Advanced Node Data) is a lightweight, framework-agnostic JavaScript library for building advanced sunburst charts with SVG.","archived":false,"fork":false,"pushed_at":"2026-01-19T14:22:47.000Z","size":410,"stargazers_count":1,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-19T19:43:38.920Z","etag":null,"topics":["framework-agnostic","npm","npm-module","sunburst","sunburst-graph","svg"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@akitain/sandjs","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Aqu1tain.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-16T08:09:21.000Z","updated_at":"2025-11-27T10:06:07.000Z","dependencies_parsed_at":"2025-10-20T15:28:36.701Z","dependency_job_id":"1d1e3305-6de9-451c-8d52-b76e220f6ea1","html_url":"https://github.com/Aqu1tain/sandjs","commit_stats":null,"previous_names":["aqu1tain/sandjs"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/Aqu1tain/sandjs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aqu1tain%2Fsandjs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aqu1tain%2Fsandjs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aqu1tain%2Fsandjs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aqu1tain%2Fsandjs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Aqu1tain","download_url":"https://codeload.github.com/Aqu1tain/sandjs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aqu1tain%2Fsandjs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28661882,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T01:17:37.254Z","status":"online","status_checked_at":"2026-01-22T02:00:07.137Z","response_time":144,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["framework-agnostic","npm","npm-module","sunburst","sunburst-graph","svg"],"created_at":"2025-09-24T10:21:01.918Z","updated_at":"2026-01-22T11:01:47.238Z","avatar_url":"https://github.com/Aqu1tain.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sand.js\n\n**Sunburst Advanced Node Data**\n\nA lightweight, framework-agnostic JavaScript library for building interactive sunburst charts using SVG. Sand.js is fully data-driven: describe your chart in JSON, and it handles both layout computation and rendering.\n\n[![npm version](https://img.shields.io/npm/v/@akitain/sandjs.svg)](https://www.npmjs.com/package/@akitain/sandjs)\n[![GitHub stars](https://img.shields.io/github/stars/aqu1tain/sandjs.svg?style=social\u0026label=Star)](https://github.com/aQu1tain/sandjs)\n\n## Documentation\n\n**[View Complete Documentation →](./docs/README.md)**\n\nFor detailed guides, API reference, and examples, visit the [full documentation](./docs/README.md).\n\n---\n\n## Table of Contents\n\n- [Introduction](#introduction)\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Core Concepts](#core-concepts)\n- [Configuration Reference](#configuration-reference)\n- [Features](#features)\n  - [Color Themes](#color-themes)\n  - [Navigation \u0026 Drilldown](#navigation--drilldown)\n  - [Tooltips](#tooltips)\n  - [Breadcrumbs](#breadcrumbs)\n  - [Highlighting](#highlighting)\n  - [Transitions](#transitions)\n  - [Labels](#labels)\n- [API Reference](#api-reference)\n- [Build \u0026 Development](#build--development)\n- [CDN Usage](#cdn-usage)\n- [Browser Support](#browser-support)\n- [License](#license)\n\n---\n\n## Introduction\n\nSand.js is designed for developers who need to visualize hierarchical data as sunburst charts with minimal setup. Built with modern web standards, it offers:\n\n- **Zero dependencies**: Lightweight and fast\n- **Framework agnostic**: Works with vanilla JavaScript or any framework\n- **JSON-driven**: Declarative configuration\n- **Interactive**: Built-in tooltips, navigation, and event callbacks\n- **Customizable**: Extensive theming and styling options\n- **TypeScript ready**: Full type definitions included\n\n---\n\n## Installation\n\n```bash\nnpm install @akitain/sandjs\n```\n\nFor Yarn users:\n\n```bash\nyarn add @akitain/sandjs\n```\n\n---\n\n## Quick Start\n\nCreate a basic sunburst chart in three steps:\n\n1. **Add an SVG element to your HTML:**\n\n```html\n\u003csvg id=\"chart\"\u003e\u003c/svg\u003e\n```\n\n2. **Define your data configuration:**\n\n```javascript\nimport { renderSVG } from '@akitain/sandjs';\n\nconst config = {\n  size: { radius: 200 },\n  layers: [\n    {\n      id: 'main',\n      radialUnits: [0, 2],\n      angleMode: 'free',\n      tree: [\n        { name: 'Engineering', value: 45, key: 'eng' },\n        {\n          name: 'Design',\n          value: 30,\n          key: 'design',\n          children: [\n            { name: 'UI', value: 15 },\n            { name: 'UX', value: 15 }\n          ]\n        },\n        { name: 'Marketing', value: 25, key: 'marketing' }\n      ]\n    }\n  ]\n};\n```\n\n3. **Render the chart:**\n\n```javascript\nconst chart = renderSVG({\n  el: '#chart',\n  config,\n  tooltip: true\n});\n```\n\nThat's it! You now have a fully interactive sunburst chart.\n\n### Simple API (Recommended for Basic Charts)\n\nFor simple sunbursts, skip the full configuration and use the `data` + `radius` shorthand:\n\n```javascript\nimport { renderSVG } from '@akitain/sandjs';\n\nconst chart = renderSVG({\n  el: '#chart',\n  radius: 200,\n  data: [\n    { name: 'Engineering', value: 45 },\n    {\n      name: 'Design',\n      value: 30,\n      children: [\n        { name: 'UI', value: 15 },\n        { name: 'UX', value: 15 }\n      ]\n    },\n    { name: 'Marketing', value: 25 }\n  ],\n  tooltip: true\n});\n```\n\nThe Simple API automatically:\n- Creates a single layer with `angleMode: 'free'`\n- Computes `radialUnits` from your tree depth\n- Sets the chart radius\n\nFor partial sunbursts (less than a full circle), add the `angle` option:\n\n```javascript\nrenderSVG({\n  el: '#chart',\n  radius: 200,\n  angle: Math.PI,  // Half circle\n  data: [...]\n});\n```\n\nUse the full `config` object when you need multiple layers, alignment modes, or advanced layout options.\n\n---\n\n## Core Concepts\n\nUnderstanding these fundamental concepts will help you build complex charts:\n\n### Sunburst\n\nThe complete chart containing one or more layers. Defined by overall size (radius and optional angle).\n\n### Layer\n\nA logical grouping of rings with a shared dataset. Layers can operate independently (`free` mode) or align with other layers (`align` mode).\n\n**Properties:**\n- `id` (string): Unique identifier\n- `radialUnits` ([number, number]): Inner and outer radial positions\n- `angleMode` ('free' | 'align'): How angular space is distributed\n- `tree` (Node | Node[]): Data structure for the layer\n\n### Node\n\nA unit of data representing a segment in your chart. Nodes can have children for hierarchical data.\n\n**Key properties:**\n- `name` (string): Display label\n- `value` (number): Size of the segment\n- `key` (string, optional): Stable identifier for animations and alignment\n- `children` (Node[], optional): Child nodes for hierarchical structure\n\n### Arc\n\nA computed geometric entity created by the layout engine, ready for rendering with coordinates and metadata.\n\n### Ring\n\nA radial band in the chart, automatically calculated based on nodes and their `expandLevels` property.\n\n### Key-group\n\nNodes sharing the same `key` value, used for alignment across layers and coordinated interactions.\n\n---\n\n## Configuration Reference\n\n### SunburstConfig\n\nThe root configuration object for your chart.\n\n```typescript\n{\n  size: {\n    radius: number;      // Final radius in pixels\n    angle?: number;      // Total angle in radians (default: 2π)\n  },\n  layers: LayerConfig[]  // Array of layer definitions\n}\n```\n\n### LayerConfig\n\n```typescript\n{\n  id: string;                          // Unique layer identifier\n  radialUnits: [number, number];       // [inner, outer] radial positions\n  angleMode: 'free' | 'align';         // Angular distribution mode\n  alignWith?: string;                  // Reference layer ID (for 'align' mode)\n  padAngle?: number;                   // Gap between arcs (radians)\n  baseOffset?: number;                 // Global rotation offset (radians)\n  arcOffsetMode?: 'relative' | 'absolute'; // Offset calculation mode\n  defaultArcOffset?: number;           // Default offset for all arcs\n  borderColor?: string;                // Border color for arcs in this layer\n  borderWidth?: number;                // Border width in pixels\n  labelColor?: string;                 // Label text color for this layer\n  showLabels?: boolean;                // Show/hide labels for this layer\n  tree: TreeNodeInput | TreeNodeInput[]; // Data structure\n}\n```\n\n### TreeNodeInput\n\n```typescript\n{\n  name: string;              // Display name\n  value: number;             // Arc size (auto-summed if children exist)\n  key?: string;              // Stable identifier\n  expandLevels?: number;     // Radial thickness in rings (default: 1)\n  offset?: number;           // Local angular offset\n  color?: string;            // Custom color (CSS format)\n  labelColor?: string;       // Custom label text color\n  children?: TreeNodeInput[]; // Child nodes\n  tooltip?: string;          // Custom tooltip content\n  collapsed?: boolean;       // Hide children while preserving value\n  hidden?: boolean;          // Hide node completely\n}\n```\n\n---\n\n## Features\n\n### Color Themes\n\nSand.js includes 14 built-in color palettes across three theme types.\n\n#### Qualitative Themes\n\nBest for categorical data with no inherent order:\n\n```javascript\nimport { renderSVG, QUALITATIVE_PALETTES } from '@akitain/sandjs';\n\nrenderSVG({\n  el: '#chart',\n  config,\n  colorTheme: {\n    type: 'qualitative',\n    palette: 'ocean',  // 'default' | 'pastel' | 'vibrant' | 'earth' | 'ocean' | 'sunset'\n    assignBy: 'key'    // Color assignment strategy\n  }\n});\n```\n\n#### Sequential Themes\n\nBest for ordered data with progression from low to high:\n\n```javascript\ncolorTheme: {\n  type: 'sequential',\n  palette: 'blues',  // 'blues' | 'greens' | 'purples' | 'oranges'\n  assignBy: 'depth'\n}\n```\n\n#### Diverging Themes\n\nBest for data with a meaningful midpoint (e.g., positive/negative values):\n\n```javascript\ncolorTheme: {\n  type: 'diverging',\n  palette: 'redBlue',  // 'redBlue' | 'orangePurple' | 'greenRed'\n  assignBy: 'value'\n}\n```\n\n#### Color Assignment Strategies\n\n- **key**: Consistent colors based on arc keys (default for qualitative)\n- **depth**: Colors vary by hierarchical depth (default for sequential/diverging)\n- **index**: Sequential assignment by arc position\n- **value**: Colors mapped to normalized values\n\n#### Custom Palettes\n\n```javascript\ncolorTheme: {\n  type: 'qualitative',\n  palette: ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f7dc6f'],\n  assignBy: 'key'\n}\n```\n\n#### Custom Color Keys\n\n```javascript\ncolorTheme: {\n  type: 'qualitative',\n  palette: 'default',\n  deriveKey: (arc) =\u003e arc.data.category  // Use any arc property\n}\n```\n\nNote: Individual `node.color` values always override theme colors.\n\n---\n\n### Navigation \u0026 Drilldown\n\nEnable interactive drill-down navigation with smooth transitions:\n\n#### Basic Usage\n\n```javascript\nconst chart = renderSVG({\n  el: '#chart',\n  config,\n  navigation: true,  // Enable with defaults\n  transition: true   // Enable smooth animations\n});\n```\n\n#### Advanced Options\n\n```javascript\nnavigation: {\n  layers: ['main', 'details'],  // Specify navigable layers\n  rootLabel: 'Home',            // Breadcrumb root text\n  focusTransition: {\n    duration: 600,               // Animation duration (ms)\n    easing: (t) =\u003e t * t        // Custom easing function\n  },\n  onFocusChange: (focus) =\u003e {\n    if (focus) {\n      console.log('Focused:', focus.arc.data.name);\n    } else {\n      console.log('Reset to root');\n    }\n  }\n}\n```\n\n#### Programmatic Control\n\n```javascript\n// Reset to root view\nchart.resetNavigation?.();\n```\n\n---\n\n### Tooltips\n\nDisplay contextual information on hover.\n\n#### Basic Tooltips\n\n```javascript\nrenderSVG({\n  el: '#chart',\n  config,\n  tooltip: true  // Enable default tooltips\n});\n```\n\n#### Custom Tooltips\n\n```javascript\ntooltip: {\n  formatter: (arc) =\u003e {\n    return `\n      \u003cstrong\u003e${arc.data.name}\u003c/strong\u003e\u003cbr\u003e\n      Value: ${arc.data.value}\u003cbr\u003e\n      Percentage: ${arc.percentage.toFixed(1)}%\n    `;\n  },\n  container: '#tooltip-container'  // Custom container selector\n}\n```\n\n#### Per-Node Tooltips\n\n```javascript\ntree: [\n  {\n    name: 'Engineering',\n    value: 45,\n    tooltip: 'Custom tooltip for Engineering department'\n  }\n]\n```\n\n---\n\n### Breadcrumbs\n\nVisualize the current navigation path.\n\n#### Basic Breadcrumbs\n\n```javascript\nrenderSVG({\n  el: '#chart',\n  config,\n  breadcrumbs: true  // Enable with defaults\n});\n```\n\n#### Advanced Configuration\n\n```javascript\nbreadcrumbs: {\n  container: '#breadcrumb-trail',  // Custom container\n  interactive: true,               // Enable click navigation\n  separator: ' › ',                // Custom separator\n  rootLabel: 'Overview',           // Root element label\n  formatter: (arc) =\u003e arc.data.name.toUpperCase()  // Custom formatting\n}\n```\n\n---\n\n### Highlighting\n\nHighlight related arcs by key.\n\n#### Basic Highlighting\n\n```javascript\nrenderSVG({\n  el: '#chart',\n  config,\n  highlightByKey: true  // Enable with defaults\n});\n```\n\n#### Advanced Options\n\n```javascript\nhighlightByKey: {\n  className: 'highlighted',  // Custom CSS class\n  pinOnClick: true,          // Keep highlight on click\n  onPinChange: (key, pinned) =\u003e {\n    console.log(`${key} is ${pinned ? 'pinned' : 'unpinned'}`);\n  }\n}\n```\n\n---\n\n### Transitions\n\nSmooth animations when updating your chart.\n\n#### Enable Transitions\n\n```javascript\nrenderSVG({\n  el: '#chart',\n  config,\n  transition: true  // Enable with defaults\n});\n```\n\n#### Custom Transition Settings\n\n```javascript\ntransition: {\n  duration: 800,           // Animation duration (ms)\n  easing: (t) =\u003e t * t,   // Easing function\n  delay: 100              // Delay before animation starts (ms)\n}\n```\n\n#### Updating with Transitions\n\n```javascript\nconst chart = renderSVG({ el: '#chart', config, transition: true });\n\n// Later, update with smooth transition\nchart.update({\n  config: newConfig,\n  transition: {\n    duration: 500\n  }\n});\n```\n\n---\n\n### Labels\n\nRender text labels on arcs.\n\n#### Enable Labels\n\n```javascript\nrenderSVG({\n  el: '#chart',\n  config,\n  labels: true  // Enable default labels\n});\n```\n\n#### Label Options\n\n```javascript\nlabels: {\n  labelPadding: 8,       // Spacing around text in pixels (default: 8)\n  labelFit: 'both',      // 'both' | 'height' | 'width'\n  fontSize: { min: 8, max: 16 },\n  autoLabelColor: true   // Contrast-aware text color\n}\n```\n\n**`labelFit`** controls which dimensions are checked when fitting labels:\n- `'both'` (default): Label must fit the arc's radial thickness and arc length\n- `'height'`: Only check radial thickness, use max font size based on ring height\n- `'width'`: Only check arc length, labels always fit along the arc path\n\n#### Custom Label Formatting\n\n```javascript\nlabels: {\n  formatter: (arc) =\u003e {\n    if (arc.percentage \u003e 10) {\n      return `${arc.data.name} (${arc.percentage.toFixed(0)}%)`;\n    }\n    return '';  // Hide labels for small arcs\n  }\n}\n```\n\nNote: Labels automatically hide on arcs that are too narrow to display text legibly.\n\n---\n\n## API Reference\n\n### renderSVG(options)\n\nMain function to create a sunburst chart.\n\n**Parameters:**\n\n```typescript\n{\n  el: string | SVGElement;           // Target SVG element or selector\n  config: SunburstConfig;            // Chart configuration\n  tooltip?: boolean | TooltipOptions; // Tooltip settings\n  breadcrumbs?: boolean | BreadcrumbOptions; // Breadcrumb settings\n  highlightByKey?: boolean | HighlightByKeyOptions; // Highlight settings\n  navigation?: boolean | NavigationOptions; // Navigation settings\n  transition?: boolean | TransitionOptions; // Transition settings\n  labels?: boolean | LabelOptions;   // Label settings\n  colorTheme?: ColorThemeOptions;    // Color theme\n  onArcEnter?: (payload) =\u003e void;    // Hover enter callback\n  onArcMove?: (payload) =\u003e void;     // Hover move callback\n  onArcLeave?: (payload) =\u003e void;    // Hover leave callback\n  onArcClick?: (payload) =\u003e void;    // Click callback\n  debug?: boolean;                   // Enable diagnostic logging\n}\n```\n\n**Returns:**\n\n```typescript\n{\n  update: (updateInput) =\u003e void;     // Update the chart\n  dispose: () =\u003e void;               // Clean up resources\n  resetNavigation?: () =\u003e void;      // Reset to root (if navigation enabled)\n}\n```\n\n### layout(config)\n\nCompute arc geometries from configuration (layout-only mode).\n\n**Parameters:**\n- `config` (SunburstConfig): Chart configuration\n\n**Returns:**\n- `LayoutArc[]`: Array of computed arcs with geometry and metadata\n\n### formatArcBreadcrumb(arc)\n\nGenerate a breadcrumb trail for an arc.\n\n**Parameters:**\n- `arc` (LayoutArc): The arc to generate breadcrumbs for\n\n**Returns:**\n- `BreadcrumbTrailItem[]`: Array of breadcrumb items\n\n---\n\n## Build \u0026 Development\n\n### Development Setup\n\n```bash\n# Clone the repository\ngit clone https://github.com/aqu1tain/sandjs.git\ncd sandjs\n\n# Install dependencies\nnpm install\n\n# Run tests\nnpm test\n\n# Build the library\nnpm run build\n\n# Run tests and build\nnpm run verify\n```\n\n### Development Server\n\n```bash\nnpm run dev\n```\n\nOpens a development server at `http://localhost:4173` with live examples.\n\n### Project Structure\n\n```\nsandjs/\n├── src/\n│   ├── index.ts           # Public API exports\n│   ├── layout/            # Layout computation\n│   ├── render/            # SVG rendering\n│   └── types/             # TypeScript definitions\n├── demo/                  # Interactive examples\n├── dist/                  # Build output (generated)\n└── tests/                 # Test suite\n```\n\n### Build Output\n\n- `dist/sandjs.mjs`: ES Module (default)\n- `dist/sandjs.iife.min.js`: Minified IIFE for CDN usage\n- `dist/index.d.ts`: TypeScript type definitions\n\n---\n\n## CDN Usage\n\nFor quick prototyping or non-bundled environments:\n\n```html\n\u003csvg id=\"chart\"\u003e\u003c/svg\u003e\n\n\u003cscript src=\"https://unpkg.com/@akitain/sandjs@1.0.0/dist/sandjs.iife.min.js\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  const { renderSVG } = window.SandJS;\n\n  renderSVG({\n    el: '#chart',\n    config: {\n      size: { radius: 200 },\n      layers: [\n        {\n          id: 'main',\n          radialUnits: [0, 2],\n          angleMode: 'free',\n          tree: [\n            { name: 'Category A', value: 40 },\n            { name: 'Category B', value: 60 }\n          ]\n        }\n      ]\n    },\n    tooltip: true\n  });\n\u003c/script\u003e\n```\n\n---\n\n## Browser Support\n\nSand.js targets ESNext and supports modern browsers:\n\n| Browser | Minimum Version |\n|---------|-----------------|\n| Chrome | 80+ |\n| Firefox | 74+ |\n| Safari | 13.1+ |\n| Edge | 80+ |\n| iOS Safari | 13.4+ |\n| Chrome Android | 80+ |\n\n**Not supported:** Internet Explorer\n\n\u003e For older browsers, transpile the bundle with Babel. See the [Browser Support Guide](./docs/guides/browser-support.md) for details.\n\n---\n\n## License\n\nMIT © Aqu1tain\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faqu1tain%2Fsandjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faqu1tain%2Fsandjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faqu1tain%2Fsandjs/lists"}